From cff29bde76ee0e73a00df0f83b4b48f83ef476e2 Mon Sep 17 00:00:00 2001 From: jagar Date: Thu, 8 Dec 2022 09:17:25 +0530 Subject: [PATCH] Added gtestsuite folder into blis repo Moved blis gtestsuite from lib-confscript to blis repo (branch: amd-main) Change-Id: If7ad391eef66bac6d26cf5223e6043d52b746072 --- gtestsuite/CMakeLists.txt | 249 ++ gtestsuite/Makefile | 211 + gtestsuite/README.md | 38 + gtestsuite/alphabeta.dat | 21 + gtestsuite/inc/blis_test.h | 519 +++ gtestsuite/inc/gtest/gtest-death-test.h | 346 ++ gtestsuite/inc/gtest/gtest-matchers.h | 930 +++++ gtestsuite/inc/gtest/gtest-message.h | 219 + gtestsuite/inc/gtest/gtest-param-test.h | 507 +++ gtestsuite/inc/gtest/gtest-printers.h | 1029 +++++ gtestsuite/inc/gtest/gtest-spi.h | 238 ++ gtestsuite/inc/gtest/gtest-test-part.h | 184 + gtestsuite/inc/gtest/gtest-typed-test.h | 329 ++ gtestsuite/inc/gtest/gtest.h | 2495 +++++++++++ gtestsuite/inc/gtest/gtest_pred_impl.h | 359 ++ gtestsuite/inc/gtest/gtest_prod.h | 61 + .../inc/gtest/internal/custom/README.md | 56 + .../inc/gtest/internal/custom/gtest-port.h | 37 + .../gtest/internal/custom/gtest-printers.h | 42 + gtestsuite/inc/gtest/internal/custom/gtest.h | 37 + .../internal/gtest-death-test-internal.h | 304 ++ .../inc/gtest/internal/gtest-filepath.h | 211 + .../inc/gtest/internal/gtest-internal.h | 1560 +++++++ .../inc/gtest/internal/gtest-param-util.h | 947 +++++ .../inc/gtest/internal/gtest-port-arch.h | 114 + gtestsuite/inc/gtest/internal/gtest-port.h | 2389 +++++++++++ gtestsuite/inc/gtest/internal/gtest-string.h | 175 + .../inc/gtest/internal/gtest-type-util.h | 183 + gtestsuite/input.general | 63 + gtestsuite/input.operations | 321 ++ gtestsuite/inputfile.txt | 3 + gtestsuite/lib/libgtest.a | Bin 0 -> 2125482 bytes gtestsuite/src/blis_api.cpp | 3583 ++++++++++++++++ gtestsuite/src/blis_api.h | 737 ++++ gtestsuite/src/blis_inpfile.cpp | 3651 +++++++++++++++++ gtestsuite/src/blis_inpfile.h | 490 +++ gtestsuite/src/blis_utils.cpp | 1947 +++++++++ gtestsuite/src/blis_utils.h | 503 +++ gtestsuite/src/blis_utils_int.cpp | 625 +++ gtestsuite/src/gtest_pthread.cpp | 62 + gtestsuite/src/gtest_pthread.h | 29 + gtestsuite/src/gtest_suite.cpp | 2379 +++++++++++ gtestsuite/src/lpgemm_utils.cpp | 112 + gtestsuite/src/lpgemm_utils.h | 481 +++ gtestsuite/src/main.cpp | 79 + gtestsuite/src/ref_addm.cpp | 130 + gtestsuite/src/ref_addv.cpp | 117 + gtestsuite/src/ref_amaxv.cpp | 105 + gtestsuite/src/ref_axpbyv.cpp | 202 + gtestsuite/src/ref_axpy2v.cpp | 231 ++ gtestsuite/src/ref_axpyf.cpp | 156 + gtestsuite/src/ref_axpym.cpp | 137 + gtestsuite/src/ref_axpyv.cpp | 160 + gtestsuite/src/ref_copym.cpp | 130 + gtestsuite/src/ref_copyv.cpp | 114 + gtestsuite/src/ref_dotaxpyv.cpp | 228 + gtestsuite/src/ref_dotv.cpp | 129 + gtestsuite/src/ref_dotxaxpyf.cpp | 245 ++ gtestsuite/src/ref_dotxf.cpp | 172 + gtestsuite/src/ref_dotxv.cpp | 197 + gtestsuite/src/ref_gemm.cpp | 317 ++ gtestsuite/src/ref_gemmt.cpp | 445 ++ gtestsuite/src/ref_gemv.cpp | 319 ++ gtestsuite/src/ref_ger.cpp | 184 + gtestsuite/src/ref_hemm.cpp | 486 +++ gtestsuite/src/ref_hemv.cpp | 305 ++ gtestsuite/src/ref_her.cpp | 203 + gtestsuite/src/ref_her2.cpp | 259 ++ gtestsuite/src/ref_her2k.cpp | 666 +++ gtestsuite/src/ref_herk.cpp | 648 +++ gtestsuite/src/ref_normfm.cpp | 109 + gtestsuite/src/ref_normfv.cpp | 107 + gtestsuite/src/ref_scal2m.cpp | 163 + gtestsuite/src/ref_scal2v.cpp | 146 + gtestsuite/src/ref_scalm.cpp | 139 + gtestsuite/src/ref_scalv.cpp | 130 + gtestsuite/src/ref_subm.cpp | 130 + gtestsuite/src/ref_subv.cpp | 117 + gtestsuite/src/ref_symm.cpp | 486 +++ gtestsuite/src/ref_symv.cpp | 305 ++ gtestsuite/src/ref_syr.cpp | 195 + gtestsuite/src/ref_syr2.cpp | 243 ++ gtestsuite/src/ref_syr2k.cpp | 611 +++ gtestsuite/src/ref_syrk.cpp | 503 +++ gtestsuite/src/ref_trmm.cpp | 651 +++ gtestsuite/src/ref_trmm3.cpp | 551 +++ gtestsuite/src/ref_trmv.cpp | 278 ++ gtestsuite/src/ref_trsm.cpp | 534 +++ gtestsuite/src/ref_trsv.cpp | 293 ++ gtestsuite/src/ref_xpbym.cpp | 176 + gtestsuite/src/ref_xpbyv.cpp | 165 + gtestsuite/src/test_addm.cpp | 235 ++ gtestsuite/src/test_addm.h | 16 + gtestsuite/src/test_addv.cpp | 216 + gtestsuite/src/test_addv.h | 18 + gtestsuite/src/test_amaxv.cpp | 477 +++ gtestsuite/src/test_amaxv.h | 15 + gtestsuite/src/test_axpbyv.cpp | 380 ++ gtestsuite/src/test_axpbyv.h | 18 + gtestsuite/src/test_axpy2v.cpp | 274 ++ gtestsuite/src/test_axpy2v.h | 19 + gtestsuite/src/test_axpyf.cpp | 268 ++ gtestsuite/src/test_axpyf.h | 18 + gtestsuite/src/test_axpym.cpp | 232 ++ gtestsuite/src/test_axpym.h | 17 + gtestsuite/src/test_axpyv.cpp | 357 ++ gtestsuite/src/test_axpyv.h | 17 + gtestsuite/src/test_copym.cpp | 189 + gtestsuite/src/test_copym.h | 16 + gtestsuite/src/test_copyv.cpp | 306 ++ gtestsuite/src/test_copyv.h | 16 + gtestsuite/src/test_dotaxpyv.cpp | 306 ++ gtestsuite/src/test_dotaxpyv.h | 20 + gtestsuite/src/test_dotv.cpp | 369 ++ gtestsuite/src/test_dotv.h | 16 + gtestsuite/src/test_dotxaxpyf.cpp | 371 ++ gtestsuite/src/test_dotxaxpyf.h | 23 + gtestsuite/src/test_dotxf.cpp | 291 ++ gtestsuite/src/test_dotxf.h | 19 + gtestsuite/src/test_dotxv.cpp | 262 ++ gtestsuite/src/test_dotxv.h | 19 + gtestsuite/src/test_gemm.cpp | 556 +++ gtestsuite/src/test_gemm.h | 20 + gtestsuite/src/test_gemm_bf16bf16f32obf16.cpp | 262 ++ gtestsuite/src/test_gemm_bf16bf16f32of32.cpp | 262 ++ gtestsuite/src/test_gemm_f32f32f32of32.cpp | 248 ++ gtestsuite/src/test_gemm_u8s8s16os16.cpp | 248 ++ gtestsuite/src/test_gemm_u8s8s16os8.cpp | 248 ++ gtestsuite/src/test_gemm_u8s8s32os32.cpp | 248 ++ gtestsuite/src/test_gemm_u8s8s32os8.cpp | 248 ++ gtestsuite/src/test_gemmt.cpp | 597 +++ gtestsuite/src/test_gemmt.h | 95 + gtestsuite/src/test_gemv.cpp | 536 +++ gtestsuite/src/test_gemv.h | 19 + gtestsuite/src/test_ger.cpp | 455 ++ gtestsuite/src/test_ger.h | 18 + gtestsuite/src/test_hemm.cpp | 551 +++ gtestsuite/src/test_hemm.h | 20 + gtestsuite/src/test_hemv.cpp | 497 +++ gtestsuite/src/test_hemv.h | 19 + gtestsuite/src/test_her.cpp | 436 ++ gtestsuite/src/test_her.h | 17 + gtestsuite/src/test_her2.cpp | 497 +++ gtestsuite/src/test_her2.h | 18 + gtestsuite/src/test_her2k.cpp | 554 +++ gtestsuite/src/test_her2k.h | 19 + gtestsuite/src/test_herk.cpp | 505 +++ gtestsuite/src/test_herk.h | 18 + gtestsuite/src/test_normfm.cpp | 199 + gtestsuite/src/test_normfm.h | 15 + gtestsuite/src/test_normfv.cpp | 296 ++ gtestsuite/src/test_normfv.h | 16 + gtestsuite/src/test_process.cpp | 1737 ++++++++ gtestsuite/src/test_randm.cpp | 227 + gtestsuite/src/test_randv.cpp | 107 + gtestsuite/src/test_scal2m.cpp | 226 + gtestsuite/src/test_scal2m.h | 17 + gtestsuite/src/test_scal2v.cpp | 222 + gtestsuite/src/test_scal2v.h | 17 + gtestsuite/src/test_scalm.cpp | 221 + gtestsuite/src/test_scalm.h | 16 + gtestsuite/src/test_scalv.cpp | 505 +++ gtestsuite/src/test_scalv.h | 16 + gtestsuite/src/test_setm.cpp | 205 + gtestsuite/src/test_setv.cpp | 201 + gtestsuite/src/test_subm.cpp | 235 ++ gtestsuite/src/test_subm.h | 16 + gtestsuite/src/test_subv.cpp | 218 + gtestsuite/src/test_subv.h | 18 + gtestsuite/src/test_symm.cpp | 551 +++ gtestsuite/src/test_symm.h | 20 + gtestsuite/src/test_symv.cpp | 497 +++ gtestsuite/src/test_symv.h | 19 + gtestsuite/src/test_syr.cpp | 420 ++ gtestsuite/src/test_syr.h | 17 + gtestsuite/src/test_syr2.cpp | 496 +++ gtestsuite/src/test_syr2.h | 18 + gtestsuite/src/test_syr2k.cpp | 552 +++ gtestsuite/src/test_syr2k.h | 19 + gtestsuite/src/test_syrk.cpp | 504 +++ gtestsuite/src/test_syrk.h | 18 + gtestsuite/src/test_trmm.cpp | 551 +++ gtestsuite/src/test_trmm.h | 20 + gtestsuite/src/test_trmm3.cpp | 358 ++ gtestsuite/src/test_trmm3.h | 20 + gtestsuite/src/test_trmv.cpp | 500 +++ gtestsuite/src/test_trmv.h | 17 + gtestsuite/src/test_trsm.cpp | 549 +++ gtestsuite/src/test_trsm.h | 20 + gtestsuite/src/test_trsv.cpp | 529 +++ gtestsuite/src/test_trsv.h | 17 + gtestsuite/src/test_xpbym.cpp | 227 + gtestsuite/src/test_xpbym.h | 17 + gtestsuite/src/test_xpbyv.cpp | 228 + gtestsuite/src/test_xpbyv.h | 17 + 195 files changed, 64114 insertions(+) create mode 100644 gtestsuite/CMakeLists.txt create mode 100644 gtestsuite/Makefile create mode 100644 gtestsuite/README.md create mode 100644 gtestsuite/alphabeta.dat create mode 100644 gtestsuite/inc/blis_test.h create mode 100644 gtestsuite/inc/gtest/gtest-death-test.h create mode 100644 gtestsuite/inc/gtest/gtest-matchers.h create mode 100644 gtestsuite/inc/gtest/gtest-message.h create mode 100644 gtestsuite/inc/gtest/gtest-param-test.h create mode 100644 gtestsuite/inc/gtest/gtest-printers.h create mode 100644 gtestsuite/inc/gtest/gtest-spi.h create mode 100644 gtestsuite/inc/gtest/gtest-test-part.h create mode 100644 gtestsuite/inc/gtest/gtest-typed-test.h create mode 100644 gtestsuite/inc/gtest/gtest.h create mode 100644 gtestsuite/inc/gtest/gtest_pred_impl.h create mode 100644 gtestsuite/inc/gtest/gtest_prod.h create mode 100644 gtestsuite/inc/gtest/internal/custom/README.md create mode 100644 gtestsuite/inc/gtest/internal/custom/gtest-port.h create mode 100644 gtestsuite/inc/gtest/internal/custom/gtest-printers.h create mode 100644 gtestsuite/inc/gtest/internal/custom/gtest.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-death-test-internal.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-filepath.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-internal.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-param-util.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-port-arch.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-port.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-string.h create mode 100644 gtestsuite/inc/gtest/internal/gtest-type-util.h create mode 100644 gtestsuite/input.general create mode 100644 gtestsuite/input.operations create mode 100644 gtestsuite/inputfile.txt create mode 100644 gtestsuite/lib/libgtest.a create mode 100644 gtestsuite/src/blis_api.cpp create mode 100644 gtestsuite/src/blis_api.h create mode 100644 gtestsuite/src/blis_inpfile.cpp create mode 100644 gtestsuite/src/blis_inpfile.h create mode 100644 gtestsuite/src/blis_utils.cpp create mode 100644 gtestsuite/src/blis_utils.h create mode 100644 gtestsuite/src/blis_utils_int.cpp create mode 100644 gtestsuite/src/gtest_pthread.cpp create mode 100644 gtestsuite/src/gtest_pthread.h create mode 100644 gtestsuite/src/gtest_suite.cpp create mode 100644 gtestsuite/src/lpgemm_utils.cpp create mode 100644 gtestsuite/src/lpgemm_utils.h create mode 100644 gtestsuite/src/main.cpp create mode 100644 gtestsuite/src/ref_addm.cpp create mode 100644 gtestsuite/src/ref_addv.cpp create mode 100644 gtestsuite/src/ref_amaxv.cpp create mode 100644 gtestsuite/src/ref_axpbyv.cpp create mode 100644 gtestsuite/src/ref_axpy2v.cpp create mode 100644 gtestsuite/src/ref_axpyf.cpp create mode 100644 gtestsuite/src/ref_axpym.cpp create mode 100644 gtestsuite/src/ref_axpyv.cpp create mode 100644 gtestsuite/src/ref_copym.cpp create mode 100644 gtestsuite/src/ref_copyv.cpp create mode 100644 gtestsuite/src/ref_dotaxpyv.cpp create mode 100644 gtestsuite/src/ref_dotv.cpp create mode 100644 gtestsuite/src/ref_dotxaxpyf.cpp create mode 100644 gtestsuite/src/ref_dotxf.cpp create mode 100644 gtestsuite/src/ref_dotxv.cpp create mode 100644 gtestsuite/src/ref_gemm.cpp create mode 100644 gtestsuite/src/ref_gemmt.cpp create mode 100644 gtestsuite/src/ref_gemv.cpp create mode 100644 gtestsuite/src/ref_ger.cpp create mode 100644 gtestsuite/src/ref_hemm.cpp create mode 100644 gtestsuite/src/ref_hemv.cpp create mode 100644 gtestsuite/src/ref_her.cpp create mode 100644 gtestsuite/src/ref_her2.cpp create mode 100644 gtestsuite/src/ref_her2k.cpp create mode 100644 gtestsuite/src/ref_herk.cpp create mode 100644 gtestsuite/src/ref_normfm.cpp create mode 100644 gtestsuite/src/ref_normfv.cpp create mode 100644 gtestsuite/src/ref_scal2m.cpp create mode 100644 gtestsuite/src/ref_scal2v.cpp create mode 100644 gtestsuite/src/ref_scalm.cpp create mode 100644 gtestsuite/src/ref_scalv.cpp create mode 100644 gtestsuite/src/ref_subm.cpp create mode 100644 gtestsuite/src/ref_subv.cpp create mode 100644 gtestsuite/src/ref_symm.cpp create mode 100644 gtestsuite/src/ref_symv.cpp create mode 100644 gtestsuite/src/ref_syr.cpp create mode 100644 gtestsuite/src/ref_syr2.cpp create mode 100644 gtestsuite/src/ref_syr2k.cpp create mode 100644 gtestsuite/src/ref_syrk.cpp create mode 100644 gtestsuite/src/ref_trmm.cpp create mode 100644 gtestsuite/src/ref_trmm3.cpp create mode 100644 gtestsuite/src/ref_trmv.cpp create mode 100644 gtestsuite/src/ref_trsm.cpp create mode 100644 gtestsuite/src/ref_trsv.cpp create mode 100644 gtestsuite/src/ref_xpbym.cpp create mode 100644 gtestsuite/src/ref_xpbyv.cpp create mode 100644 gtestsuite/src/test_addm.cpp create mode 100644 gtestsuite/src/test_addm.h create mode 100644 gtestsuite/src/test_addv.cpp create mode 100644 gtestsuite/src/test_addv.h create mode 100644 gtestsuite/src/test_amaxv.cpp create mode 100644 gtestsuite/src/test_amaxv.h create mode 100644 gtestsuite/src/test_axpbyv.cpp create mode 100644 gtestsuite/src/test_axpbyv.h create mode 100644 gtestsuite/src/test_axpy2v.cpp create mode 100644 gtestsuite/src/test_axpy2v.h create mode 100644 gtestsuite/src/test_axpyf.cpp create mode 100644 gtestsuite/src/test_axpyf.h create mode 100644 gtestsuite/src/test_axpym.cpp create mode 100644 gtestsuite/src/test_axpym.h create mode 100644 gtestsuite/src/test_axpyv.cpp create mode 100644 gtestsuite/src/test_axpyv.h create mode 100644 gtestsuite/src/test_copym.cpp create mode 100644 gtestsuite/src/test_copym.h create mode 100644 gtestsuite/src/test_copyv.cpp create mode 100644 gtestsuite/src/test_copyv.h create mode 100644 gtestsuite/src/test_dotaxpyv.cpp create mode 100644 gtestsuite/src/test_dotaxpyv.h create mode 100644 gtestsuite/src/test_dotv.cpp create mode 100644 gtestsuite/src/test_dotv.h create mode 100644 gtestsuite/src/test_dotxaxpyf.cpp create mode 100644 gtestsuite/src/test_dotxaxpyf.h create mode 100644 gtestsuite/src/test_dotxf.cpp create mode 100644 gtestsuite/src/test_dotxf.h create mode 100644 gtestsuite/src/test_dotxv.cpp create mode 100644 gtestsuite/src/test_dotxv.h create mode 100644 gtestsuite/src/test_gemm.cpp create mode 100644 gtestsuite/src/test_gemm.h create mode 100644 gtestsuite/src/test_gemm_bf16bf16f32obf16.cpp create mode 100644 gtestsuite/src/test_gemm_bf16bf16f32of32.cpp create mode 100644 gtestsuite/src/test_gemm_f32f32f32of32.cpp create mode 100644 gtestsuite/src/test_gemm_u8s8s16os16.cpp create mode 100644 gtestsuite/src/test_gemm_u8s8s16os8.cpp create mode 100644 gtestsuite/src/test_gemm_u8s8s32os32.cpp create mode 100644 gtestsuite/src/test_gemm_u8s8s32os8.cpp create mode 100644 gtestsuite/src/test_gemmt.cpp create mode 100644 gtestsuite/src/test_gemmt.h create mode 100644 gtestsuite/src/test_gemv.cpp create mode 100644 gtestsuite/src/test_gemv.h create mode 100644 gtestsuite/src/test_ger.cpp create mode 100644 gtestsuite/src/test_ger.h create mode 100644 gtestsuite/src/test_hemm.cpp create mode 100644 gtestsuite/src/test_hemm.h create mode 100644 gtestsuite/src/test_hemv.cpp create mode 100644 gtestsuite/src/test_hemv.h create mode 100644 gtestsuite/src/test_her.cpp create mode 100644 gtestsuite/src/test_her.h create mode 100644 gtestsuite/src/test_her2.cpp create mode 100644 gtestsuite/src/test_her2.h create mode 100644 gtestsuite/src/test_her2k.cpp create mode 100644 gtestsuite/src/test_her2k.h create mode 100644 gtestsuite/src/test_herk.cpp create mode 100644 gtestsuite/src/test_herk.h create mode 100644 gtestsuite/src/test_normfm.cpp create mode 100644 gtestsuite/src/test_normfm.h create mode 100644 gtestsuite/src/test_normfv.cpp create mode 100644 gtestsuite/src/test_normfv.h create mode 100644 gtestsuite/src/test_process.cpp create mode 100644 gtestsuite/src/test_randm.cpp create mode 100644 gtestsuite/src/test_randv.cpp create mode 100644 gtestsuite/src/test_scal2m.cpp create mode 100644 gtestsuite/src/test_scal2m.h create mode 100644 gtestsuite/src/test_scal2v.cpp create mode 100644 gtestsuite/src/test_scal2v.h create mode 100644 gtestsuite/src/test_scalm.cpp create mode 100644 gtestsuite/src/test_scalm.h create mode 100644 gtestsuite/src/test_scalv.cpp create mode 100644 gtestsuite/src/test_scalv.h create mode 100644 gtestsuite/src/test_setm.cpp create mode 100644 gtestsuite/src/test_setv.cpp create mode 100644 gtestsuite/src/test_subm.cpp create mode 100644 gtestsuite/src/test_subm.h create mode 100644 gtestsuite/src/test_subv.cpp create mode 100644 gtestsuite/src/test_subv.h create mode 100644 gtestsuite/src/test_symm.cpp create mode 100644 gtestsuite/src/test_symm.h create mode 100644 gtestsuite/src/test_symv.cpp create mode 100644 gtestsuite/src/test_symv.h create mode 100644 gtestsuite/src/test_syr.cpp create mode 100644 gtestsuite/src/test_syr.h create mode 100644 gtestsuite/src/test_syr2.cpp create mode 100644 gtestsuite/src/test_syr2.h create mode 100644 gtestsuite/src/test_syr2k.cpp create mode 100644 gtestsuite/src/test_syr2k.h create mode 100644 gtestsuite/src/test_syrk.cpp create mode 100644 gtestsuite/src/test_syrk.h create mode 100644 gtestsuite/src/test_trmm.cpp create mode 100644 gtestsuite/src/test_trmm.h create mode 100644 gtestsuite/src/test_trmm3.cpp create mode 100644 gtestsuite/src/test_trmm3.h create mode 100644 gtestsuite/src/test_trmv.cpp create mode 100644 gtestsuite/src/test_trmv.h create mode 100644 gtestsuite/src/test_trsm.cpp create mode 100644 gtestsuite/src/test_trsm.h create mode 100644 gtestsuite/src/test_trsv.cpp create mode 100644 gtestsuite/src/test_trsv.h create mode 100644 gtestsuite/src/test_xpbym.cpp create mode 100644 gtestsuite/src/test_xpbym.h create mode 100644 gtestsuite/src/test_xpbyv.cpp create mode 100644 gtestsuite/src/test_xpbyv.h diff --git a/gtestsuite/CMakeLists.txt b/gtestsuite/CMakeLists.txt new file mode 100644 index 000000000..2e5387a82 --- /dev/null +++ b/gtestsuite/CMakeLists.txt @@ -0,0 +1,249 @@ +##Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved.## + +cmake_minimum_required(VERSION 3.10.0) +set(CMAKE_CXX_COMPILER ${CXX_COMPILER}) + +project(Blis_GtestSuite) + +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.1 +) +#set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) +set(BUILD_GTEST ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +include(GoogleTest) + +enable_testing() + +# Set the path to the BLIS installation. +if(NOT(BLIS_PATH)) + message(FATAL_ERROR "Need to provide a BLIS installation path during CMake invocation. Please use + $ cmake .. -DBLIS_PATH=/home/username/blis_installation") +endif() +# Set the path to BLIS include directory. +set(BLIS_INCLUDE ${BLIS_PATH}/include/blis) + +# Set OpenMP as the default option +set(ENABLE_THREADING "openmp" CACHE STRING "Setting OpenMP as the threading library") +# Set the possible values of theading libraries for cmake-gui +set_property(CACHE ENABLE_THREADING PROPERTY STRINGS "openmp" "pthreads" "no") + +# Set static BLIS as the default library we build against. +set(BLIS_LINKING_TYPE "static" CACHE STRING "Linking to a static BLIS library") +# Set the possible values of BLIS linking type for cmake-gui +set_property(CACHE BLIS_LINKING_TYPE PROPERTY STRINGS "static" "shared") + +if(BLIS_LINKING_TYPE STREQUAL "shared") + message(FATAL_ERROR "Using shared BLIS library is currently disabled.") +endif() + +option(ENABLE_VALGRIND "Run tests using valgrind" OFF) + +option(ENABLE_ASAN "Run tests using Address Sanatizer" OFF) + +# Set variable if the platform is Linux based. +if(UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() + +# Throw an error if the platform is Apple. +if(APPLE) + message(FATAL_ERROR "Build system does not support Apple platform.") +endif() + +# Set the include paths. +set(INC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/inc) + +# Set compiler options and BLIS library for Linux. +if(LINUX) + # Add compiler definition. + + add_compile_options(-g -Wall -Wno-unused-function -Wfatal-errors -fPIC) + + if(ENABLE_ASAN) + add_definitions(-D__GTEST_VALGRIND_TEST__) + add_compile_options(-fsanitize=address -static-libsan) + endif() + + # Set GNU OpenMP library as the default option + set(OpenMP_LIBRARY "GNU" CACHE STRING "Using GNU OpenMP library") + # Set the possibe values of OpenMP runtimes + set_property(CACHE OpenMP_LIBRARY PROPERTY STRINGS "GNU" "Intel") + + if(ENABLE_THREADING STREQUAL "no") + if(BLIS_LINKING_TYPE STREQUAL "static") + set(Blis_LIBRARY "${BLIS_PATH}/lib/libblis.a" CACHE STRING "blis library path") + else() + set(Blis_LIBRARY "${BLIS_PATH}/lib/libblis.so" CACHE STRING "blis library path") + endif() + else() + if(BLIS_LINKING_TYPE STREQUAL "static") + set(Blis_LIBRARY "${BLIS_PATH}/lib/libblis-mt.a" CACHE STRING "blis library path") + else() + set(Blis_LIBRARY "${BLIS_PATH}/lib/libblis-mt.so" CACHE STRING "blis library path") + endif() + endif() +endif() + +# Set BLIS library for Windows. +if(WIN32) + if(ENABLE_THREADING STREQUAL "no") + if(BLIS_LINKING_TYPE STREQUAL "static") + set(Blis_LIBRARY "${BLIS_PATH}/bin/AOCL-LibBlis-Win.a" CACHE STRING "blis library path") + else() + set(Blis_LIBRARY "${BLIS_PATH}/bin/AOCL-LibBlis-Win-dll.lib" CACHE STRING "blis library path") + endif() + else() + if(BLIS_LINKING_TYPE STREQUAL "static") + set(Blis_LIBRARY "${BLIS_PATH}/bin/AOCL-LibBlis-Win-MT.a" CACHE STRING "blis library path") + else() + set(Blis_LIBRARY "${BLIS_PATH}/bin/AOCL-LibBlis-Win-MT-dll.lib" CACHE STRING "blis library path") + endif() + endif() +endif() + +# Since this is an out-of-source build we need to copy the input files in the correct destination. +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.general + ${CMAKE_CURRENT_SOURCE_DIR}/alphabeta.dat + ${CMAKE_CURRENT_SOURCE_DIR}/input.operations + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(gtest_libblis + ${CMAKE_CURRENT_SOURCE_DIR}/src/gtest_suite.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/gtest_pthread.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/blis_api.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/blis_inpfile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/blis_utils_int.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/blis_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_process.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_addv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_amaxv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_axpbyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_axpyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_copyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_dotv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_dotxv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_normfv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_scalv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_scal2v.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_subv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_xpbyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_addm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_axpym.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_copym.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_normfm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_scalm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_scal2m.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_subm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_xpbym.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_axpy2v.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_dotaxpyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_axpyf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_dotxf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_dotxaxpyf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_gemv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_ger.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_hemv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_her.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_her2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_symv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_syr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_syr2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_trmv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_trsv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_gemm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_gemmt.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_hemm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_herk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_her2k.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_symm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_syrk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_syr2k.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_trmm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_trmm3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ref_trsm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_randv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_randm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_addv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_amaxv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_axpbyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_axpyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_copyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_dotv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_dotxv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_normfv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_scalv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_scal2v.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_setv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_subv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_xpbyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_addm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_axpym.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_copym.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_normfm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_scalm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_scal2m.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_setm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_subm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_xpbym.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_axpy2v.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_dotaxpyv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_axpyf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_dotxf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_dotxaxpyf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_ger.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_hemv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_her.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_her2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_symv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_syr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_syr2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_trmv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_trsv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemmt.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_hemm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_herk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_her2k.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_symm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_syrk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_syr2k.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_trmm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_trmm3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_trsm.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/lpgemm_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_u8s8s32os32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_u8s8s32os8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_f32f32f32of32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_u8s8s16os16.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_u8s8s16os8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_bf16bf16f32of32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test_gemm_bf16bf16f32obf16.cpp +) + +target_include_directories(gtest_libblis PUBLIC ${INC_PATH} ${BLIS_INCLUDE}) +target_link_libraries(gtest_libblis gtest gtest_main ${Blis_LIBRARY} pthread) + +# Linking appropriate threading library. +if(ENABLE_THREADING STREQUAL "openmp") + if(LINUX) + if(OpenMP_LIBRARY STREQUAL "GNU") + target_link_libraries(gtest_libblis -fopenmp) + else() + target_link_libraries(gtest_libblis iomp5) + endif() + endif() +endif() + +add_test( + NAME gtest_libblis + COMMAND gtest_libblis +) + +#gtest_discover_tests(gtest_libblis) \ No newline at end of file diff --git a/gtestsuite/Makefile b/gtestsuite/Makefile new file mode 100644 index 000000000..11426efc4 --- /dev/null +++ b/gtestsuite/Makefile @@ -0,0 +1,211 @@ +################################################################################################ +## Compiler options +################################################################################################ +ifeq ($(AOCL),1) +CXX := clang++ #-stdlib=libc++ +else +CXX := g++ -std=c++11 +endif +CFLAGS := -c + + +# Flags passed to the C++ compiler. +CXXFLAGS += -g -Wall -Wno-unused-function -Wfatal-errors -fPIC -fnon-call-exceptions +#Enable macro __GTEST_VALGRIND_TEST__ only when valgrind tool is used +CXXFLAGS += #-D__GTEST_VALGRIND_TEST__ +#Enable below flags only when Address Sanitizer tool is used +CXXFLAGS += #-fsanitize=address -static-libsan +LIBPTHREAD := -fopenmp #-lpthread +LIBM := -lm +LDFLAGS := $(LIBM) $(LIBPTHREAD) + +BLIS_PATH := ../blis_gcc +BLIS_LIB := ${BLIS_PATH}/lib/libblis-mt.a +GTEST_LIB := ./lib/libgtest.a +INC_PATH := ./inc +LINUX_PATH := ${BLIS_PATH}/include/blis +GTEST_HEADERS := ./inc/gtest + +ifeq ($(mkl),1) +CXXFLAGS +=-D_POSIX_C_SOURCE=200112L +CXXFLAGS += -std=c++11 -DBLAS=\"mkl\" +LDFLAGS += -lrt +MKL_LIB_PATH := ${MKLROOT}/intel64 +# MKL +#MKL_LIB := -L$(MKL_LIB_PATH) \ +# -lmkl_intel_lp64 \ +# -lmkl_core \ +# -lmkl_sequential \ +# -lpthread -lm -ldl + +# Uncomment below lines & comment above lines to link with multi-threaded library. +MKL_LIB := -L$(MKL_LIB_PATH) \ + -lmkl_intel_lp64 \ + -lmkl_core \ + -lmkl_gnu_thread \ + -lpthread -lm -ldl -liomp5 +endif +LIB_PATH := $(MKL_LIB) $(BLIS_LIB) $(GTEST_LIB) +################################################################################################ +## Variables +################################################################################################ +BIN_PATH := bin +TEST_OBJ_PATH := obj +TEST_SRC_PATH := src +CPPS := $(shell ls $(TEST_SRC_PATH)/*.cpp) +TEMP := $(subst $(TEST_SRC_PATH)/, $(TEST_OBJ_PATH)/, $(CPPS)) +OBJS := $(subst .cpp, .o, $(TEMP)) +HEADERS := $(shell ls inc/*.h) +INCLUDE_PATH := -I$(INC_PATH) -I$(LINUX_PATH) -I$(GTEST_HEADERS) + + +TESTSUITE_OUT_FILE := output.testsuite +ifneq ($(mkl),1) +TEST_EXE := $(BIN_PATH)/libblis_gtest +else +TEST_EXE := $(BIN_PATH)/gtest_mkl +endif + + +TEST_OBJS := $(TEST_OBJ_PATH)/gtest_suite.o \ + $(TEST_OBJ_PATH)/gtest_pthread.o \ + $(TEST_OBJ_PATH)/blis_api.o \ + $(TEST_OBJ_PATH)/blis_inpfile.o \ + $(TEST_OBJ_PATH)/blis_utils_int.o \ + $(TEST_OBJ_PATH)/blis_utils.o \ + $(TEST_OBJ_PATH)/main.o \ + $(TEST_OBJ_PATH)/test_process.o \ + $(TEST_OBJ_PATH)/ref_addv.o \ + $(TEST_OBJ_PATH)/ref_amaxv.o \ + $(TEST_OBJ_PATH)/ref_axpbyv.o \ + $(TEST_OBJ_PATH)/ref_axpyv.o \ + $(TEST_OBJ_PATH)/ref_copyv.o \ + $(TEST_OBJ_PATH)/ref_dotv.o \ + $(TEST_OBJ_PATH)/ref_dotxv.o \ + $(TEST_OBJ_PATH)/ref_normfv.o \ + $(TEST_OBJ_PATH)/ref_scalv.o \ + $(TEST_OBJ_PATH)/ref_scal2v.o \ + $(TEST_OBJ_PATH)/ref_subv.o \ + $(TEST_OBJ_PATH)/ref_xpbyv.o \ + $(TEST_OBJ_PATH)/ref_addm.o \ + $(TEST_OBJ_PATH)/ref_axpym.o \ + $(TEST_OBJ_PATH)/ref_copym.o \ + $(TEST_OBJ_PATH)/ref_normfm.o \ + $(TEST_OBJ_PATH)/ref_scalm.o \ + $(TEST_OBJ_PATH)/ref_scal2m.o \ + $(TEST_OBJ_PATH)/ref_subm.o \ + $(TEST_OBJ_PATH)/ref_xpbym.o \ + $(TEST_OBJ_PATH)/ref_axpy2v.o \ + $(TEST_OBJ_PATH)/ref_dotaxpyv.o \ + $(TEST_OBJ_PATH)/ref_axpyf.o \ + $(TEST_OBJ_PATH)/ref_dotxf.o \ + $(TEST_OBJ_PATH)/ref_dotxaxpyf.o \ + $(TEST_OBJ_PATH)/ref_gemv.o \ + $(TEST_OBJ_PATH)/ref_ger.o \ + $(TEST_OBJ_PATH)/ref_hemv.o \ + $(TEST_OBJ_PATH)/ref_her.o \ + $(TEST_OBJ_PATH)/ref_her2.o \ + $(TEST_OBJ_PATH)/ref_symv.o \ + $(TEST_OBJ_PATH)/ref_syr.o \ + $(TEST_OBJ_PATH)/ref_syr2.o \ + $(TEST_OBJ_PATH)/ref_trmv.o \ + $(TEST_OBJ_PATH)/ref_trsv.o \ + $(TEST_OBJ_PATH)/ref_gemm.o \ + $(TEST_OBJ_PATH)/ref_gemmt.o \ + $(TEST_OBJ_PATH)/ref_hemm.o \ + $(TEST_OBJ_PATH)/ref_herk.o \ + $(TEST_OBJ_PATH)/ref_her2k.o \ + $(TEST_OBJ_PATH)/ref_symm.o \ + $(TEST_OBJ_PATH)/ref_syrk.o \ + $(TEST_OBJ_PATH)/ref_syr2k.o \ + $(TEST_OBJ_PATH)/ref_trmm.o \ + $(TEST_OBJ_PATH)/ref_trmm3.o \ + $(TEST_OBJ_PATH)/ref_trsm.o \ + $(TEST_OBJ_PATH)/test_randv.o \ + $(TEST_OBJ_PATH)/test_randm.o \ + $(TEST_OBJ_PATH)/test_addv.o \ + $(TEST_OBJ_PATH)/test_amaxv.o \ + $(TEST_OBJ_PATH)/test_axpbyv.o \ + $(TEST_OBJ_PATH)/test_axpyv.o \ + $(TEST_OBJ_PATH)/test_copyv.o \ + $(TEST_OBJ_PATH)/test_dotv.o \ + $(TEST_OBJ_PATH)/test_dotxv.o \ + $(TEST_OBJ_PATH)/test_normfv.o \ + $(TEST_OBJ_PATH)/test_scalv.o \ + $(TEST_OBJ_PATH)/test_scal2v.o \ + $(TEST_OBJ_PATH)/test_setv.o \ + $(TEST_OBJ_PATH)/test_subv.o \ + $(TEST_OBJ_PATH)/test_xpbyv.o \ + $(TEST_OBJ_PATH)/test_addm.o \ + $(TEST_OBJ_PATH)/test_axpym.o \ + $(TEST_OBJ_PATH)/test_copym.o \ + $(TEST_OBJ_PATH)/test_normfm.o \ + $(TEST_OBJ_PATH)/test_scalm.o \ + $(TEST_OBJ_PATH)/test_scal2m.o \ + $(TEST_OBJ_PATH)/test_setm.o \ + $(TEST_OBJ_PATH)/test_subm.o \ + $(TEST_OBJ_PATH)/test_xpbym.o \ + $(TEST_OBJ_PATH)/test_axpy2v.o \ + $(TEST_OBJ_PATH)/test_dotaxpyv.o \ + $(TEST_OBJ_PATH)/test_axpyf.o \ + $(TEST_OBJ_PATH)/test_dotxf.o \ + $(TEST_OBJ_PATH)/test_dotxaxpyf.o \ + $(TEST_OBJ_PATH)/test_gemv.o \ + $(TEST_OBJ_PATH)/test_ger.o \ + $(TEST_OBJ_PATH)/test_hemv.o \ + $(TEST_OBJ_PATH)/test_her.o \ + $(TEST_OBJ_PATH)/test_her2.o \ + $(TEST_OBJ_PATH)/test_symv.o \ + $(TEST_OBJ_PATH)/test_syr.o \ + $(TEST_OBJ_PATH)/test_syr2.o \ + $(TEST_OBJ_PATH)/test_trmv.o \ + $(TEST_OBJ_PATH)/test_trsv.o \ + $(TEST_OBJ_PATH)/test_gemm.o \ + $(TEST_OBJ_PATH)/test_gemmt.o \ + $(TEST_OBJ_PATH)/test_hemm.o \ + $(TEST_OBJ_PATH)/test_herk.o \ + $(TEST_OBJ_PATH)/test_her2k.o \ + $(TEST_OBJ_PATH)/test_symm.o \ + $(TEST_OBJ_PATH)/test_syrk.o \ + $(TEST_OBJ_PATH)/test_syr2k.o \ + $(TEST_OBJ_PATH)/test_trmm.o \ + $(TEST_OBJ_PATH)/test_trmm3.o \ + $(TEST_OBJ_PATH)/test_trsm.o \ + $(TEST_OBJ_PATH)/lpgemm_utils.o \ + $(TEST_OBJ_PATH)/test_gemm_u8s8s16os8.o \ + $(TEST_OBJ_PATH)/test_gemm_u8s8s32os8.o \ + $(TEST_OBJ_PATH)/test_gemm_u8s8s16os16.o \ + $(TEST_OBJ_PATH)/test_gemm_u8s8s32os32.o \ + $(TEST_OBJ_PATH)/test_gemm_f32f32f32of32.o \ + $(TEST_OBJ_PATH)/test_gemm_bf16bf16f32of32.o \ + $(TEST_OBJ_PATH)/test_gemm_bf16bf16f32obf16.o + +#all: build run + +build: $(TEST_EXE) + +$(shell mkdir -p $(TEST_OBJ_PATH) $(BIN_PATH)) + +$(TEST_EXE): $(TEST_OBJS) $(HEADERS) + @echo "------------------------------------------" + @echo "Creating the executable for the Program" + @echo "------------------------------------------" + $(CXX) $(CXXFLAGS) $(INCLUDE_PATH) $(TEST_OBJS) $(LIB_PATH) $(LDFLAGS) -o $(TEST_EXE) + +$(TEST_OBJ_PATH)/%.o: $(TEST_SRC_PATH)/%.cpp + @echo "------------------------------------------" + @echo "Compiling the file $<" + @echo "------------------------------------------" + $(CXX) $(CFLAGS) $(CXXFLAGS) $(INCLUDE_PATH) $< $(LIBPTHREAD) -o $@ + + +# -- Test run/check rules -- +run: $(TEST_EXE) +# @echo "Running $(TEST_EXE) with output redirected to '$(TESTSUITE_OUT_FILE)'" + @./$(TEST_EXE) > $(TESTSUITE_OUT_FILE) + @./$(TEST_EXE) + + +clean: + rm -rf $(TEST_OBJ_PATH)/*.o $(TEST_EXE) + rm -rf $(TEST_OBJ_PATH) $(BIN_PATH) diff --git a/gtestsuite/README.md b/gtestsuite/README.md new file mode 100644 index 000000000..f494861c9 --- /dev/null +++ b/gtestsuite/README.md @@ -0,0 +1,38 @@ +Steps to build gtestsuite executable on linux +1. Set BLIS_PATH( blis installation path ) in gtestsuite Makefile. +2. To build executable + GCC compiler : type "make" + AOCC compiler : type "make AOCL=1" + executable will be generated in bin folder. +3. Set input parameters in input.general file and select api's from input.operations file. +4. Finally run the executable + $./bin/libblis_gtest + +Steps to build gtestsuite executable and run valdrind tool +1. In gtestsuite Makefile, define the macro/enable "__GTEST_VALGRIND_TEST__(-D__GTEST_VALGRIND_TEST__)" + Note : Undefine the macro "__GTEST_VALGRIND_TEST__" when it is not built for valgrind test. +2. Generate the executable as mentioned above(Steps to build gtestsuite executable) +3. Set input parameters and select api's to test in input.general and input.operations respectively +4. Finally run the executable + $ OMP_NUM_THREADS=1 valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes -s + +Steps to build blis library and gtestsuite executable and run ASAN +1. Build blis library with the flag CFLAGS="-g -fsanitize=address" + CC=clang ./configure -a aocl_gemm --enable-threading=openmp --enable-cblas CFLAGS="-g -fsanitize=address" auto +2. And in gtestsuite Makefile, define the macro/enable "__GTEST_VALGRIND_TEST__(-D__GTEST_VALGRIND_TEST__)" + and even set/enable "CXXFLAGS += -fsanitize=address -static-libsan". +3. Generate the executable as mentioned above(Steps to build gtestsuite executable) +4. Set input parameters in input.general file and select api's from input.operations file. +5. Finally run the executable + $./bin/libblis_gtest + +Steps to build gtestsuite executable for mkl library(machine lib-daytonax-04) +1. Goto gtestsuite folder and export the following (paths depends on the machine, where intel package is placed) + export LD_LIBRARY_PATH=/home/intel2019/update_05/intel64/:$LD_LIBRARY_PATH + export MKLROOT=/home/intel2019/update_05/ + export MKL_DEBUG_CPU_TYPE=5 + export MKL_ENABLE_INSTRUCTIONS=AVX2 +2. Type "make mkl=1" or "make AOCL=1 mkl=1(for clang compiler)", executable will be generated in bin folder +3. Set input parameters in input.general file and select api's from input.operations file. +4. Finally run the executable + $./bin/gtest_mkl diff --git a/gtestsuite/alphabeta.dat b/gtestsuite/alphabeta.dat new file mode 100644 index 000000000..bcd6f3a0c --- /dev/null +++ b/gtestsuite/alphabeta.dat @@ -0,0 +1,21 @@ +# -------------------------------------------------------------------------- +# +# alphabeta.dat +# BLIS test suite +# +# This file contains alpha and beta values which are used +# in BLIS operations +# +# -------------------------------------------------------------------------- + +5 #Number of alpha and/or beta values +-1.000 0.000 #const double alpha[5] = {-1.000,0.000,1.000,0.999,2.333}; +0.000 0.000 +1.000 0.000 +0.999 0.000 +2.333 0.000 +-1.000 0.000 #const double beta[5] = {-1.000,0.000,1.000,0.999,2.333}; +0.000 0.000 +1.000 0.000 +0.999 0.000 +2.333 0.000 \ No newline at end of file diff --git a/gtestsuite/inc/blis_test.h b/gtestsuite/inc/blis_test.h new file mode 100644 index 000000000..873151023 --- /dev/null +++ b/gtestsuite/inc/blis_test.h @@ -0,0 +1,519 @@ +#ifndef BLIS_TEST_H +#define BLIS_TEST_H + +#include +#include +#include +#include +#include + +#include "blis.h" +#include "gtest/gtest.h" + +using namespace std; + +// --- System headers --------------------------------------------------------- +// For va_* functions. +#include +// For string manipulation functions. +#include +// For other string manipulation functions (e.g. isspace()). +#include + +// For POSIX stuff. +#ifndef _MSC_VER +#include +#endif + +// --- Constants and types ---------------------------------------------------- +#define PARAMETERS_FILENAME "input.general" +#define OPERATIONS_FILENAME "input.operations" +#define ALPHA_BETA_FILENAME "alphabeta.dat" +#define INPUT_COMMENT_CHAR '#' +#define OUTPUT_COMMENT_CHAR '%' + +#define BLIS_FILE_PREFIX_STR "libblis_test" +#define BLIS_FILEDATA_PREFIX_STR "blis" +#define BLAS_FILEDATA_PREFIX_STR "blas" +#define CBLAS_FILEDATA_PREFIX_STR "cblas" + +#define INPUT_BUFFER_SIZE 256 +#define MAX_FILENAME_LENGTH 1000 +#define MAX_BINARY_NAME_LENGTH 256 +#define MAX_FUNC_STRING_LENGTH 36 +#define FLOPS_PER_UNIT_PERF 1e9 + +#define MAX_NUM_MSTORAGE 4 +#define MAX_NUM_VSTORAGE 5 +#define MAX_NUM_DATATYPES 4 +#define MAX_NUM_PARAMETERS 7 +#define MAX_NUM_DIMENSIONS 3 +#define MAX_NUM_OPERANDS 5 + +#define MAX_PASS_STRING_LENGTH 32 +#define BLIS_TEST_FAIL_STRING "FAILURE" +#define BLIS_TEST_WARN_STRING "MARGINAL" +#define BLIS_TEST_PASS_STRING "PASS" +#define BLIS_TEST_OVERFLOW_STRING "OVERFLOW" +#define BLIS_TEST_UNDERFLOW_STRING "UNDERFLOW" + +#define ON_FAILURE_IGNORE_CHAR 'i' +#define ON_FAILURE_SLEEP_CHAR 's' +#define ON_FAILURE_ABORT_CHAR 'a' + +#define SECONDS_TO_SLEEP 3 + +#define DISABLE 0 +#define ENABLE 1 +#define ENABLE_ONLY 2 + +#define MAX_PARAM_VALS_PER_TYPE 4 +#define BLIS_TEST_PARAM_SIDE_CHARS "lr" +#define BLIS_TEST_PARAM_UPLO_CHARS "lu" +#define BLIS_TEST_PARAM_UPLODE_CHARS "dlu" +#define BLIS_TEST_PARAM_TRANS_CHARS "ncth" +#define BLIS_TEST_PARAM_CONJ_CHARS "nc" +#define BLIS_TEST_PARAM_DIAG_CHARS "nu" + +#define BLIS_INIT_SUCCESS 0 +#define BLIS_INIT_FAILURE -1 +#define NUM_PARAM_TYPES 6 +#define MAX_NUM_ABVALUES 5 + +/*Allocating buffers with malloc in gtestsuite */ +#define __GTESTSUITE_MALLOC_BUFFER__ + +typedef enum +{ + BLIS_TEST_PARAM_SIDE = 0, + BLIS_TEST_PARAM_UPLO = 1, + BLIS_TEST_PARAM_UPLODE = 2, + BLIS_TEST_PARAM_TRANS = 3, + BLIS_TEST_PARAM_CONJ = 4, + BLIS_TEST_PARAM_DIAG = 5, +} param_t; + +#define MAX_STORE_VALS_PER_TYPE 4 +#define BLIS_TEST_MSTORE_CHARS "crg" +#define BLIS_TEST_VSTORE_CHARS "crji" + +#define NUM_OPERAND_TYPES 2 +typedef enum +{ + BLIS_TEST_MATRIX_OPERAND = 0, + BLIS_TEST_VECTOR_OPERAND = 1 +} operand_t; + +typedef enum +{ + API_BLIS = 0, + API_CBLAS = 1, + API_BLAS = 2 +} api_t; + +typedef enum +{ + BLIS_DEFAULT = 0, + BLIS_OVERFLOW = 1, + BLIS_UNDERFLOW = 2 +} vflg_t; + +typedef enum +{ + BLIS_TEST_DIMS_MNK = 0, + BLIS_TEST_DIMS_MN = 1, + BLIS_TEST_DIMS_MK = 2, + BLIS_TEST_DIMS_M = 3, + BLIS_TEST_DIMS_MF = 4, + BLIS_TEST_DIMS_K = 5, + BLIS_TEST_NO_DIMS = 6 +} dimset_t; + +typedef enum +{ + BLIS_TEST_SEQ_UKERNEL = 0, + BLIS_TEST_SEQ_FRONT_END = 1, + BLIS_TEST_MT_FRONT_END = 2 +} iface_t; + + +typedef enum +{ + BLIS_TEST_RAND_REAL_VALUES = 0, + BLIS_TEST_RAND_NARROW_POW2 = 1 +} rand_t; + +typedef struct +{ + double failwarn; + double warnpass; +} thresh_t; + +const thresh_t thresh[BLIS_NUM_FP_TYPES] = { { 1e-04, 1e-05 }, // warn, pass for s + { 1e-04, 1e-05 }, // warn, pass for c + { 1e-13, 1e-14 }, // warn, pass for d + { 1e-13, 1e-14 } }; // warn, pass for z + +#define SIZE 4 +typedef dcomplex atom_t; + +typedef struct +{ + dim_t m; + dim_t n; + dim_t k; +} tensor_t; + +typedef struct +{ + unsigned int n_repeats; + unsigned int n_mstorage; + unsigned int n_vstorage; + char storage[ NUM_OPERAND_TYPES ][ MAX_NUM_MSTORAGE + 1 ]; + unsigned int mix_all_storage; + unsigned int alignment; + unsigned int rand_method; + unsigned int gs_spacing; + unsigned int n_datatypes; + char datatype_char[ MAX_NUM_DATATYPES + 1 ]; + num_t datatype[ MAX_NUM_DATATYPES + 1 ]; + unsigned int mixed_domain; + unsigned int mixed_precision; + unsigned int p_first; + unsigned int p_max; + unsigned int p_inc; + unsigned int ind_enable[ BLIS_NUM_IND_METHODS ]; + unsigned int n_app_threads; + char reaction_to_failure; + unsigned int output_matlab_format; + unsigned int output_files; + unsigned int error_checking_level; + unsigned int is_mixed_dt; + unsigned int n_param_combos; + unsigned int n_store_combos; + unsigned int n_dt_combos; + char** pc_str; + char** sc_str; + char** dc_str; + unsigned int indim[MAX_NUM_DATATYPES][BLIS_NAT+1]; + unsigned int indn[MAX_NUM_DATATYPES]; + num_t dt[MAX_NUM_DATATYPES]; + bool initparams; + + api_t api; + unsigned int abf; + atom_t *alpha; + atom_t *beta; + unsigned int nab; + unsigned int ldf; + unsigned int ld[3]; + unsigned int bitextf; + unsigned int dimf; + unsigned int ndim; + unsigned int nanf; + tensor_t *dim; + + unsigned int passflag; + vflg_t oruflw; + unsigned int bitrp; + + char op_t; + +} test_params_t; + +typedef struct +{ + char libblis_test_parameters_filename[ MAX_FILENAME_LENGTH + 1 ]; + char libblis_test_operations_filename[ MAX_FILENAME_LENGTH + 1 ]; + char libblis_test_alphabeta_parameter[ MAX_FILENAME_LENGTH + 1 ]; +} blis_string_t; + +typedef struct +{ + // parent test_ops_t struct + struct test_ops_s* ops; + + opid_t opid; + dimset_t dimset; + + int op_switch; + unsigned int n_dims; + + int dim_spec[ MAX_NUM_DIMENSIONS ]; + int dim_aux[ MAX_NUM_DIMENSIONS ]; + unsigned int n_params; + char params[ MAX_NUM_PARAMETERS ]; + bool test_done; +} test_op_t; + +typedef struct test_ops_s +{ + // individual override + int indiv_over; + + // section overrides + int util_over; + int l1v_over; + int l1m_over; + int l1f_over; + int l2_over; + int l3ukr_over; + int l3_over; + + // util + test_op_t randv; + test_op_t randm; + + // level-1v + test_op_t addv; + test_op_t amaxv; + test_op_t axpbyv; + test_op_t axpyv; + test_op_t copyv; + test_op_t dotv; + test_op_t dotxv; + test_op_t normfv; + test_op_t scalv; + test_op_t scal2v; + test_op_t setv; + test_op_t subv; + test_op_t xpbyv; + + // level-1m + test_op_t addm; + test_op_t axpym; + test_op_t copym; + test_op_t normfm; + test_op_t scalm; + test_op_t scal2m; + test_op_t setm; + test_op_t subm; + test_op_t xpbym; + + // level-1f + test_op_t axpy2v; + test_op_t dotaxpyv; + test_op_t axpyf; + test_op_t dotxf; + test_op_t dotxaxpyf; + + // level-2 + test_op_t gemv; + test_op_t ger; + test_op_t hemv; + test_op_t her; + test_op_t her2; + test_op_t symv; + test_op_t syr; + test_op_t syr2; + test_op_t trmv; + test_op_t trsv; + + // level-3 micro-kernels + test_op_t gemm_ukr; + test_op_t trsm_ukr; + test_op_t gemmtrsm_ukr; + + // level-3 + test_op_t gemm; + test_op_t gemmt; + test_op_t hemm; + test_op_t herk; + test_op_t her2k; + test_op_t symm; + test_op_t syrk; + test_op_t syr2k; + test_op_t trmm; + test_op_t trmm3; + test_op_t trsm; + + test_op_t gemm_u8s8s32os32; + test_op_t gemm_u8s8s32os8; + test_op_t gemm_f32f32f32of32; + test_op_t gemm_u8s8s16os16; + test_op_t gemm_u8s8s16os8; + test_op_t gemm_bf16bf16f32of32; + test_op_t gemm_bf16bf16f32obf16; + +} test_ops_t; + +typedef struct +{ + uint32_t tcnt; + uint32_t cntf; +} printres_t; + +typedef struct +{ + test_params_t* params; + test_op_t* op; + const char* str; + unsigned int nt; + unsigned int id; + iface_t iface; + unsigned int xc; + bli_pthread_barrier_t* barrier; + printres_t* pfr; +} thread_data_t; + +typedef struct +{ + char inputfile[ MAX_FILENAME_LENGTH ]; + int fileread; +} input_file_t; + +typedef struct +{ + test_params_t *params; + test_ops_t *ops; + input_file_t *pfile; + bli_pthread_t *pthread; + thread_data_t *tdata; +} input_data_t; + +void* libblis_test_randv_thread_entry( void* tdata_void ); +void* libblis_test_randm_thread_entry( void* tdata_void ); + +void* libblis_test_addv_thread_entry( void* tdata_void ); +void* libblis_test_amaxv_thread_entry( void* tdata_void ); +void* libblis_test_axpbyv_thread_entry( void* tdata_void ); +void* libblis_test_axpyv_thread_entry( void* tdata_void ); +void* libblis_test_copyv_thread_entry( void* tdata_void ); +void* libblis_test_dotv_thread_entry( void* tdata_void ); +void* libblis_test_dotxv_thread_entry( void* tdata_void ); +void* libblis_test_normfv_thread_entry( void* tdata_void ); +void* libblis_test_scal2v_thread_entry( void* tdata_void ); +void* libblis_test_scalv_thread_entry( void* tdata_void ); +void* libblis_test_setv_thread_entry( void* tdata_void ); +void* libblis_test_subv_thread_entry( void* tdata_void ); + +void* libblis_test_xpbyv_thread_entry( void* tdata_void ); +void* libblis_test_axpy2v_thread_entry( void* tdata_void ); +void* libblis_test_dotaxpyv_thread_entry( void* tdata_void ); +void* libblis_test_axpyf_thread_entry( void* tdata_void ); +void* libblis_test_dotxf_thread_entry( void* tdata_void ); +void* libblis_test_dotxaxpyf_thread_entry( void* tdata_void ); + +void* libblis_test_addm_thread_entry( void* tdata_void ); +void* libblis_test_axpym_thread_entry( void* tdata_void ); +void* libblis_test_copym_thread_entry( void* tdata_void ); +void* libblis_test_normfm_thread_entry( void* tdata_void ); +void* libblis_test_scal2m_thread_entry( void* tdata_void ); +void* libblis_test_scalm_thread_entry( void* tdata_void ); +void* libblis_test_setm_thread_entry( void* tdata_void ); +void* libblis_test_subm_thread_entry( void* tdata_void ); +void* libblis_test_xpbym_thread_entry( void* tdata_void ); + +void* libblis_test_gemv_thread_entry( void* tdata_void ); +void* libblis_test_ger_thread_entry( void* tdata_void ); +void* libblis_test_hemv_thread_entry( void* tdata_void ); +void* libblis_test_her_thread_entry( void* tdata_void ); +void* libblis_test_her2_thread_entry( void* tdata_void ); +void* libblis_test_symv_thread_entry( void* tdata_void ); +void* libblis_test_syr_thread_entry( void* tdata_void ); +void* libblis_test_syr2_thread_entry( void* tdata_void ); +void* libblis_test_trmv_thread_entry( void* tdata_void ); +void* libblis_test_trsv_thread_entry( void* tdata_void ); + +void* libblis_test_gemm_thread_entry( void* tdata_void ); +void* libblis_test_gemmt_thread_entry( void* tdata_void ); +void* libblis_test_hemm_thread_entry( void* tdata_void ); +void* libblis_test_herk_thread_entry( void* tdata_void ); +void* libblis_test_her2k_thread_entry( void* tdata_void ); +void* libblis_test_symm_thread_entry( void* tdata_void ); +void* libblis_test_syrk_thread_entry( void* tdata_void ); +void* libblis_test_syr2k_thread_entry( void* tdata_void ); +void* libblis_test_trmm_thread_entry( void* tdata_void ); +void* libblis_test_trmm3_thread_entry( void* tdata_void ); +void* libblis_test_trsm_thread_entry( void* tdata_void ); + +void* libblis_test_gemm_u8s8s32os32_thread_entry( void* tdata_void ); +void* libblis_test_gemm_f32f32f32of32_thread_entry( void* tdata_void ); +void* libblis_test_gemm_u8s8s16os8_thread_entry( void* tdata_void ); +void* libblis_test_gemm_u8s8s32os8_thread_entry( void* tdata_void ); +void* libblis_test_gemm_u8s8s16os16_thread_entry( void* tdata_void ); +void* libblis_test_gemm_bf16bf16f32of32_thread_entry( void* tdata_void ); +void* libblis_test_gemm_bf16bf16f32obf16_thread_entry( void* tdata_void ); + +/* + * The derived class for Blis Test Suite + * where all the data members and member functions are + * declared and defined + */ +class AoclBlisTestFixture : public ::testing::TestWithParam +{ + public: + void SetUp() override + { + params = GetParam().params; + ops = GetParam().ops; + pthread = GetParam().pthread; + tdata = GetParam().tdata; + pfile = GetParam().pfile; + + if(pfile->fileread != 1) + { + nt = ( unsigned int )params->n_app_threads; + barrier = + (bli_pthread_barrier_t*)bli_malloc_user( sizeof( bli_pthread_barrier_t ) ); + bli_pthread_barrier_init( barrier, NULL, nt ); + } + pfr = (printres_t*)bli_malloc_user( sizeof( printres_t ) ); + memset(pfr, 0, sizeof(printres_t)); + } + + void TearDown() override + { + if(pfile->fileread != 1) + { + bli_pthread_barrier_destroy( barrier ); + bli_free_user( barrier ); + } + bli_free_user( pfr ); + } + + bool libblis_test_preprocess_params( test_params_t* params, test_op_t* op, + iface_t iface, const char* p_types, const char* o_types); + + bool create_params(test_params_t *params); + + bool destroy_params(test_params_t *params); + + int libblis_test_read_params_inpfile( char* filename, test_params_t* params, + test_ops_t* ops, printres_t* pfr); + protected: + unsigned int nt; + input_data_t* inData; + test_params_t* params; + test_ops_t* ops; + tensor_t* dim; + bli_pthread_t* pthread; + thread_data_t* tdata; + printres_t* pfr; + input_file_t* pfile; + bli_pthread_barrier_t* barrier; +}; + +class BlisTestSuite +{ + private: + blis_string_t blis_string; + input_file_t pfile; + test_params_t params; + test_ops_t ops; + public: + ~BlisTestSuite( ); + test_params_t* getParamsStr() { return &(this->params); } + blis_string_t* getStgStr() { return &(this->blis_string); } + test_ops_t* getOpsStr() { return &(this->ops); } + input_file_t* getfileStr() { return &(this->pfile); } + + int libblis_test_init_strings(blis_string_t *test_data); + int libblis_test_inpfile( char* input_filename, input_file_t* pfile); + int libblis_test_read_params_file( char* input_filename, + test_params_t* params, char *abpf); + int libblis_test_read_ops_file( char* input_filename, test_ops_t* ops ); + void CreateGtestFilters(test_ops_t* ops, string& str); + void CreateGtestFilters_api(input_file_t* pfile, string& str); +}; +#endif // BLIS_TEST_H diff --git a/gtestsuite/inc/gtest/gtest-death-test.h b/gtestsuite/inc/gtest/gtest-death-test.h new file mode 100644 index 000000000..9b4d4d133 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-death-test.h @@ -0,0 +1,346 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include "gtest/internal/gtest-death-test-internal.h" + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// The final parameter to each of these macros is a matcher applied to any data +// the sub-process wrote to stderr. For compatibility with existing tests, a +// bare string is interpreted as a regular expression matcher. +// +// On the regular expressions used in death tests: +// +// GOOGLETEST_CM0005 DO NOT DELETE +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// + +// Asserts that a given `statement` causes the program to exit, with an +// integer exit status that satisfies `predicate`, and emitting error output +// that matches `matcher`. +# define ASSERT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_) + +// Like `ASSERT_EXIT`, but continues on to successive tests in the +// test suite, if any: +# define EXPECT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given `statement` causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches `matcher`. +# define ASSERT_DEATH(statement, matcher) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Like `ASSERT_DEATH`, but continues on to successive tests in the +// test suite, if any: +# define EXPECT_DEATH(statement, matcher) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + ExitedWithCode(const ExitedWithCode&) = default; + void operator=(const ExitedWithCode& other) = delete; + bool operator()(int exit_status) const; + private: + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Tests that an exit code describes an exit due to termination by a +// given signal. +// GOOGLETEST_CM0006 DO NOT DELETE +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters +// on systems that support death tests. This allows one to write such a macro on +// a system that does not support death tests and be sure that it will compile +// on a death-test supporting system. It is exposed publicly so that systems +// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST +// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and +// ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter if and only if EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/gtestsuite/inc/gtest/gtest-matchers.h b/gtestsuite/inc/gtest/gtest-matchers.h new file mode 100644 index 000000000..9fa34a05b --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-matchers.h @@ -0,0 +1,930 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GTEST_MAYBE_5046_ 5046 +#else +#define GTEST_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherMatcher that implements the matcher interface: +// using is_gtest_matcher = void; +// bool MatchAndExplain(const T&, std::ostream*); +// (MatchResultListener* can also be used instead of std::ostream*) +// void DescribeTo(std::ostream*); +// void DescribeNegationTo(std::ostream*); +// +// 2. a factory function that creates a Matcher object from a +// FooMatcherMatcher. + +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != nullptr) *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true if and only if the listener is interested in an explanation + // of the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != nullptr; } + + private: + ::std::ostream* const stream_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); +}; + +inline MatchResultListener::~MatchResultListener() { +} + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class GTEST_API_ MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() {} + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Test) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +namespace internal { + +struct AnyEq { + template + bool operator()(const A& a, const B& b) const { return a == b; } +}; +struct AnyNe { + template + bool operator()(const A& a, const B& b) const { return a != b; } +}; +struct AnyLt { + template + bool operator()(const A& a, const B& b) const { return a < b; } +}; +struct AnyGt { + template + bool operator()(const A& a, const B& b) const { return a > b; } +}; +struct AnyLe { + template + bool operator()(const A& a, const B& b) const { return a <= b; } +}; +struct AnyGe { + template + bool operator()(const A& a, const B& b) const { return a >= b; } +}; + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(nullptr) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); +}; + +struct SharedPayloadBase { + std::atomic ref{1}; + void Ref() { ref.fetch_add(1, std::memory_order_relaxed); } + bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; } +}; + +template +struct SharedPayload : SharedPayloadBase { + explicit SharedPayload(const T& v) : value(v) {} + explicit SharedPayload(T&& v) : value(std::move(v)) {} + + static void Destroy(SharedPayloadBase* shared) { + delete static_cast(shared); + } + + T value; +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase : private MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener'. + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + GTEST_CHECK_(vtable_ != nullptr); + return vtable_->match_and_explain(*this, x, listener); + } + + // Returns true if and only if this matcher matches x. + bool Matches(const T& x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, false); + } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, true); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(const T& x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + if (vtable_ == nullptr) return nullptr; + return vtable_->get_describer(*this); + } + + protected: + MatcherBase() : vtable_(nullptr) {} + + // Constructs a matcher from its implementation. + template + explicit MatcherBase(const MatcherInterface* impl) { + Init(impl); + } + + template ::type::is_gtest_matcher> + MatcherBase(M&& m) { // NOLINT + Init(std::forward(m)); + } + + MatcherBase(const MatcherBase& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + if (IsShared()) buffer_.shared->Ref(); + } + + MatcherBase& operator=(const MatcherBase& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + if (IsShared()) buffer_.shared->Ref(); + return *this; + } + + MatcherBase(MatcherBase&& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + other.vtable_ = nullptr; + } + + MatcherBase& operator=(MatcherBase&& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + other.vtable_ = nullptr; + return *this; + } + + ~MatcherBase() override { Destroy(); } + + private: + struct VTable { + bool (*match_and_explain)(const MatcherBase&, const T&, + MatchResultListener*); + void (*describe)(const MatcherBase&, std::ostream*, bool negation); + // Returns the captured object if it implements the interface, otherwise + // returns the MatcherBase itself. + const MatcherDescriberInterface* (*get_describer)(const MatcherBase&); + // Called on shared instances when the reference count reaches 0. + void (*shared_destroy)(SharedPayloadBase*); + }; + + bool IsShared() const { + return vtable_ != nullptr && vtable_->shared_destroy != nullptr; + } + + // If the implementation uses a listener, call that. + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) { + return P::Get(m).MatchAndExplain(value, listener->stream()); + } + + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener)) { + return P::Get(m).MatchAndExplain(value, listener); + } + + template + static void DescribeImpl(const MatcherBase& m, std::ostream* os, + bool negation) { + if (negation) { + P::Get(m).DescribeNegationTo(os); + } else { + P::Get(m).DescribeTo(os); + } + } + + template + static const MatcherDescriberInterface* GetDescriberImpl( + const MatcherBase& m) { + // If the impl is a MatcherDescriberInterface, then return it. + // Otherwise use MatcherBase itself. + // This allows us to implement the GetDescriber() function without support + // from the impl, but some users really want to get their impl back when + // they call GetDescriber(). + // We use std::get on a tuple as a workaround of not having `if constexpr`. + return std::get<( + std::is_convertible::value + ? 1 + : 0)>(std::make_tuple(&m, &P::Get(m))); + } + + template + const VTable* GetVTable() { + static constexpr VTable kVTable = {&MatchAndExplainImpl

, + &DescribeImpl

, &GetDescriberImpl

, + P::shared_destroy}; + return &kVTable; + } + + union Buffer { + // Add some types to give Buffer some common alignment/size use cases. + void* ptr; + double d; + int64_t i; + // And add one for the out-of-line cases. + SharedPayloadBase* shared; + }; + + void Destroy() { + if (IsShared() && buffer_.shared->Unref()) { + vtable_->shared_destroy(buffer_.shared); + } + } + + template + static constexpr bool IsInlined() { + return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) && + std::is_trivially_copy_constructible::value && + std::is_trivially_destructible::value; + } + + template ()> + struct ValuePolicy { + static const M& Get(const MatcherBase& m) { + // When inlined along with Init, need to be explicit to avoid violating + // strict aliasing rules. + const M *ptr = static_cast( + static_cast(&m.buffer_)); + return *ptr; + } + static void Init(MatcherBase& m, M impl) { + ::new (static_cast(&m.buffer_)) M(impl); + } + static constexpr auto shared_destroy = nullptr; + }; + + template + struct ValuePolicy { + using Shared = SharedPayload; + static const M& Get(const MatcherBase& m) { + return static_cast(m.buffer_.shared)->value; + } + template + static void Init(MatcherBase& m, Arg&& arg) { + m.buffer_.shared = new Shared(std::forward(arg)); + } + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + struct ValuePolicy*, B> { + using M = const MatcherInterface; + using Shared = SharedPayload>; + static const M& Get(const MatcherBase& m) { + return *static_cast(m.buffer_.shared)->value; + } + static void Init(MatcherBase& m, M* impl) { + m.buffer_.shared = new Shared(std::unique_ptr(impl)); + } + + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + void Init(M&& m) { + using MM = typename std::decay::type; + using Policy = ValuePolicy; + vtable_ = GetVTable(); + Policy::Init(*this, std::forward(m)); + } + + const VTable* vtable_; + Buffer buffer_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a std::shared_ptr to const +// MatcherInterface. Don't inherit from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + explicit Matcher() {} // NOLINT + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template + explicit Matcher( + const MatcherInterface* impl, + typename std::enable_if::value>::type* = + nullptr) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) : internal::MatcherBase(std::forward(m)) {} // NOLINT + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) { + } + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +// Prints a matcher in a human-readable format. +template +std::ostream& operator<<(std::ostream& os, const Matcher& matcher) { + matcher.DescribeTo(&os); + return os; +} + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); } + + void DescribeNegationTo(::std::ostream* os) const override { + impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + }; + + Impl impl_; +}; + +// Creates a matcher from its implementation. +// DEPRECATED: Especially in the generic code, prefer: +// Matcher(new MyMatcherImpl(...)); +// +// MakeMatcher may create a Matcher that accepts its argument by value, which +// leads to unnecessary copies & lack of support for non-copyable types. +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +namespace internal { +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +template +class ComparisonBase { + public: + explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} + + using is_gtest_matcher = void; + + template + bool MatchAndExplain(const Lhs& lhs, std::ostream*) const { + return Op()(lhs, Unwrap(rhs_)); + } + void DescribeTo(std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + void DescribeNegationTo(std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + + private: + template + static const T& Unwrap(const T& v) { + return v; + } + template + static const T& Unwrap(std::reference_wrapper v) { + return v; + } + + Rhs rhs_; +}; + +template +class EqMatcher : public ComparisonBase, Rhs, AnyEq> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyEq>(rhs) { } + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template +class NeMatcher : public ComparisonBase, Rhs, AnyNe> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyNe>(rhs) { } + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template +class LtMatcher : public ComparisonBase, Rhs, AnyLt> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLt>(rhs) { } + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template +class GtMatcher : public ComparisonBase, Rhs, AnyGt> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGt>(rhs) { } + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template +class LeMatcher : public ComparisonBase, Rhs, AnyLe> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLe>(rhs) { } + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template +class GeMatcher : public ComparisonBase, Rhs, AnyGe> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGe>(rhs) { } + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +template ::value>::type> +using StringLike = T; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + return MatchAndExplain(std::string(s), listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(std::string(s), listener); + } + + // Matches anything that can convert to std::string. + // + // This is a template, not just a plain function with const std::string&, + // because absl::string_view has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const std::string& s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) + : RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const std::shared_ptr regex_; + const bool full_match_; +}; +} // namespace internal + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +template +PolymorphicMatcher MatchesRegex( + const internal::StringLike& regex) { + return MatchesRegex(new internal::RE(std::string(regex))); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +template +PolymorphicMatcher ContainsRegex( + const internal::StringLike& regex) { + return ContainsRegex(new internal::RE(std::string(regex))); +} + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template +inline internal::EqMatcher Eq(T x) { return internal::EqMatcher(x); } + +// Constructs a Matcher from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template +Matcher::Matcher(T value) { *this = Eq(value); } + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq(x) is just a convenient short-hand for Matcher(Eq(x)) +// or Matcher(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher(Lt(5)) to be explicit about the type, +// for example. +template +inline Matcher TypedEq(const Rhs& rhs) { return Eq(rhs); } + +// Creates a polymorphic matcher that matches anything >= x. +template +inline internal::GeMatcher Ge(Rhs x) { + return internal::GeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template +inline internal::GtMatcher Gt(Rhs x) { + return internal::GtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template +inline internal::LeMatcher Le(Rhs x) { + return internal::LeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template +inline internal::LtMatcher Lt(Rhs x) { + return internal::LtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template +inline internal::NeMatcher Ne(Rhs x) { + return internal::NeMatcher(x); +} +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ diff --git a/gtestsuite/inc/gtest/gtest-message.h b/gtestsuite/inc/gtest/gtest-message.h new file mode 100644 index 000000000..becfd49fc --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-message.h @@ -0,0 +1,219 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == nullptr) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + // We'll hold the text streamed to this object here. + const std::unique_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/gtestsuite/inc/gtest/gtest-param-test.h b/gtestsuite/inc/gtest/gtest-param-test.h new file mode 100644 index 000000000..804e70281 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-param-test.h @@ -0,0 +1,507 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) +// +// GOOGLETEST_CM0001 DO NOT DELETE +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test suite +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more than once) the first argument to the +// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the +// actual test suite name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests +// in the given test suite, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_SUITE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test suite is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test suite FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test suite StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test suite StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_SUITE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_SUITE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename std::iterator_traits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename std::iterator_traits::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test suite BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_SUITE_P(NumSequence, +// BarTest, +// Values("one", "two", "three")); +// +// This instantiates tests from test suite BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// +template +internal::ValueArray Values(T... v) { + return internal::ValueArray(std::move(v)...); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test suite FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// std::tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// std::tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder Combine(const Generator&... g) { + return internal::CartesianProductHolder(g...); +} + +#define TEST_P(test_suite_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + void TestBody() override; \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify +// generator and an optional function or functor that generates custom test name +// suffixes based on the test parameters. Such a function or functor should +// accept one argument of type testing::TestParamInfo, and +// return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define GTEST_EXPAND_(arg) arg +#define GTEST_GET_FIRST_(first, ...) first +#define GTEST_GET_SECOND_(first, second, ...) second + +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))))(info); \ + } \ + static int gtest_##prefix##test_suite_name##_dummy_ \ + GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + + +// Allow Marking a Parameterized test class as not needing to be instantiated. +#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \ + namespace gtest_do_not_use_outside_namespace_scope {} \ + static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \ + GTEST_STRINGIFY_(T)) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TEST_CASE_P \ + static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \ + ""); \ + INSTANTIATE_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/gtestsuite/inc/gtest/gtest-printers.h b/gtestsuite/inc/gtest/gtest-printers.h new file mode 100644 index 000000000..076c9de1f --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-printers.h @@ -0,0 +1,1029 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Definitions in the internal* namespaces are subject to change without notice. +// DO NOT USE THEM IN USER CODE! +namespace internal { + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +struct ContainerPrinter { + template (0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value>::type> + static void PrintValue(const T& container, std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (auto&& elem : container) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(elem, os) here as PrintTo() doesn't + // handle `elem` being a native array. + internal::UniversalPrint(elem, os); + ++count; + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; + } +}; + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +struct FunctionPointerPrinter { + template ::value>::type> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); + } + } +}; + +struct PointerPrinter { + template + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } + } +}; + +namespace internal_stream_operator_without_lexical_name_lookup { + +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); + +struct StreamPrinter { + template ::value>::type, + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + typename = decltype(std::declval() + << std::declval())> + static void PrintValue(const T& value, ::std::ostream* os) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. + *os << value; + } +}; + +} // namespace internal_stream_operator_without_lexical_name_lookup + +struct ProtobufPrinter { + // We print a protobuf using its ShortDebugString() when the string + // doesn't exceed this many characters; otherwise we print it using + // DebugString() for better readability. + static const size_t kProtobufOneLinerMaxLength = 50; + + template ::value>::type> + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +struct ConvertibleToIntegerPrinter { + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(internal::BiggestInt value, ::std::ostream* os) { + *os << value; + } +}; + +struct ConvertibleToStringViewPrinter { +#if GTEST_INTERNAL_HAS_STRING_VIEW + static void PrintValue(internal::StringView value, ::std::ostream* os) { + internal::UniversalPrint(value, os); + } +#endif +}; + + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); +struct RawBytesPrinter { + // SFINAE on `sizeof` to make sure we have a complete type. + template + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo( + static_cast( + // Load bearing cast to void* to support iOS + reinterpret_cast(std::addressof(value))), + sizeof(value), os); + } +}; + +struct FallbackPrinter { + template + static void PrintValue(const T&, ::std::ostream* os) { + *os << "(incomplete type)"; + } +}; + +// Try every printer in order and return the first one that works. +template +struct FindFirstPrinter : FindFirstPrinter {}; + +template +struct FindFirstPrinter< + T, decltype(Printer::PrintValue(std::declval(), nullptr)), + Printer, Printers...> { + using type = Printer; +}; + +// Select the best printer in the following order: +// - Print containers (they have begin/end/etc). +// - Print function pointers. +// - Print object pointers. +// - Use the stream operator, if available. +// - Print protocol buffers. +// - Print types convertible to BiggestInt. +// - Print types convertible to StringView, if available. +// - Fallback to printing the raw bytes of the object. +template +void PrintWithFallback(const T& value, ::std::ostream* os) { + using Printer = typename FindFirstPrinter< + T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ProtobufPrinter, ConvertibleToIntegerPrinter, + ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type; + Printer::PrintValue(value, os); +} + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); +#ifdef __cpp_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); +#ifdef __cpp_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string); + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + internal::PrintWithFallback(value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); +inline void PrintTo(char16_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#ifdef __cpp_char8_t +inline void PrintTo(char8_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#endif + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#ifdef __cpp_char8_t +// Overloads for u8 strings. +GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); +inline void PrintTo(char8_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif +// Overloads for u16 strings. +GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os); +inline void PrintTo(char16_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +// Overloads for u32 strings. +GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os); +inline void PrintTo(char32_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::std::string. +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::std::u8string +#ifdef __cpp_char8_t +GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { + PrintU8StringTo(s, os); +} +#endif + +// Overloads for ::std::u16string +GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) { + PrintU16StringTo(s, os); +} + +// Overloads for ::std::u32string +GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) { + PrintU32StringTo(s, os); +} + +// Overloads for ::std::wstring. +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Overload for internal::StringView. +inline void PrintTo(internal::StringView sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } + +template +void PrintTo(std::reference_wrapper ref, ::std::ostream* os) { + UniversalPrinter::Print(ref.get(), os); +} + +inline const void* VoidifyPointer(const void* p) { return p; } +inline const void* VoidifyPointer(volatile const void* p) { + return const_cast(p); +} + +template +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + // We can't print the value. Just print the pointer.. + *os << "(" << (VoidifyPointer)(ptr.get()) << ")"; + } +} +template ::value && + !std::is_array::value>::type> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = "; + UniversalPrinter::Print(*ptr, os); + *os << ")"; + } +} + +template +void PrintTo(const std::unique_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +template +void PrintTo(const std::shared_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T&, std::integral_constant, + ::std::ostream*) {} + +template +void PrintTupleTo(const T& t, std::integral_constant, + ::std::ostream* os) { + PrintTupleTo(t, std::integral_constant(), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (I > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter::type>::Print( + std::get(t), os); +} + +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + *os << "("; + PrintTupleTo(t, std::integral_constant(), os); + *os << ")"; +} + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Remove any const-qualifiers before passing a type to UniversalPrinter. +template +class UniversalPrinter : public UniversalPrinter {}; + +#if GTEST_INTERNAL_HAS_ANY + +// Printer for std::any / absl::any + +template <> +class UniversalPrinter { + public: + static void Print(const Any& value, ::std::ostream* os) { + if (value.has_value()) { + *os << "value of type " << GetTypeName(value); + } else { + *os << "no value"; + } + } + + private: + static std::string GetTypeName(const Any& value) { +#if GTEST_HAS_RTTI + return internal::GetTypeName(value.type()); +#else + static_cast(value); // possibly unused + return ""; +#endif // GTEST_HAS_RTTI + } +}; + +#endif // GTEST_INTERNAL_HAS_ANY + +#if GTEST_INTERNAL_HAS_OPTIONAL + +// Printer for std::optional / absl::optional + +template +class UniversalPrinter> { + public: + static void Print(const Optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +#endif // GTEST_INTERNAL_HAS_OPTIONAL + +#if GTEST_INTERNAL_HAS_VARIANT + +// Printer for std::variant / absl::variant + +template +class UniversalPrinter> { + public: + static void Print(const Variant& value, ::std::ostream* os) { + *os << '('; +#if GTEST_HAS_ABSL + absl::visit(Visitor{os, value.index()}, value); +#else + std::visit(Visitor{os, value.index()}, value); +#endif // GTEST_HAS_ABSL + *os << ')'; + } + + private: + struct Visitor { + template + void operator()(const U& u) const { + *os << "'" << GetTypeName() << "(index = " << index + << ")' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + std::size_t index; + }; +}; + +#endif // GTEST_INTERNAL_HAS_VARIANT + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +#ifdef __cpp_char8_t +// This overload prints a (const) char8_t array compactly. +GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, + ::std::ostream* os); +#endif + +// This overload prints a (const) char16_t array compactly. +GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) char32_t array compactly. +GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter : public UniversalTersePrinter { +}; + +#ifdef __cpp_char8_t +template <> +class UniversalTersePrinter { + public: + static void Print(const char8_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u8string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(const char16_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u16string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +template <> +class UniversalTersePrinter { + public: + static void Print(const char32_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u32string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +typedef ::std::vector< ::std::string> Strings; + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. +template +void TersePrintPrefixToStrings(const Tuple&, std::integral_constant, + Strings*) {} +template +void TersePrintPrefixToStrings(const Tuple& t, + std::integral_constant, + Strings* strings) { + TersePrintPrefixToStrings(t, std::integral_constant(), + strings); + ::std::stringstream ss; + UniversalTersePrint(std::get(t), &ss); + strings->push_back(ss.str()); +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TersePrintPrefixToStrings( + value, std::integral_constant::value>(), + &result); + return result; +} + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gtest/internal/custom/gtest-printers.h" + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/gtestsuite/inc/gtest/gtest-spi.h b/gtestsuite/inc/gtest/gtest-spi.h new file mode 100644 index 000000000..eacef4466 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-spi.h @@ -0,0 +1,238 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +// GOOGLETEST_CM0004 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include "gtest/gtest.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/gtestsuite/inc/gtest/gtest-test-part.h b/gtestsuite/inc/gtest/gtest-test-part.h new file mode 100644 index 000000000..203fdf98c --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-test-part.h @@ -0,0 +1,184 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure, // Failed and the test should be terminated. + kSkip // Skipped. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, const char* a_file_name, int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == nullptr ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) {} + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? nullptr : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true if and only if the test part was skipped. + bool skipped() const { return type_ == kSkip; } + + // Returns true if and only if the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true if and only if the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true if and only if the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + // Returns true if and only if the test part failed. + bool failed() const { return fatally_failed() || nonfatally_failed(); } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class GTEST_API_ TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + ~HasNewFatalFailureHelper() override; + void ReportTestPartResult(const TestPartResult& result) override; + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ diff --git a/gtestsuite/inc/gtest/gtest-typed-test.h b/gtestsuite/inc/gtest/gtest-typed-test.h new file mode 100644 index 000000000..9fdc6be10 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest-typed-test.h @@ -0,0 +1,329 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test suite, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_SUITE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_SUITE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test suite as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +// TYPED_TEST_SUITE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template +// static std::string GetName(int) { +// if (std::is_same()) return "char"; +// if (std::is_same()) return "int"; +// if (std::is_same()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test suite +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_SUITE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test suite as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test suite name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test suite name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_SUITE above, +// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" + +// Implements typed tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test suite. +#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_ + +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestSuiteName) \ + gtest_type_params_##TestSuiteName##_NameGenerator + +#define TYPED_TEST_SUITE(CaseName, Types, ...) \ + typedef ::testing::internal::GenerateTypeList::type \ + GTEST_TYPE_PARAMS_(CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +#define TYPED_TEST(CaseName, TestName) \ + static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \ + "test-name must not be empty"); \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##CaseName##_##TestName##_registered_ \ + GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + GTEST_STRINGIFY_(CaseName), \ + GTEST_STRINGIFY_(TestName), 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)::TestBody() + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE \ + static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \ + TYPED_TEST_SUITE +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Implements type-parameterized tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test suite are defined in. The exact +// name of the namespace is subject to change without notice. +#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test suite. +#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \ + gtest_typed_test_suite_p_state_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test suite. +#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \ + gtest_registered_test_names_##TestSuiteName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +#define TYPED_TEST_SUITE_P(SuiteName) \ + static ::testing::internal::TypedTestSuitePState \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE_P \ + static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \ + TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template \ + class TestName : public SuiteName { \ + private: \ + typedef SuiteName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template \ + void GTEST_SUITE_NAMESPACE_( \ + SuiteName)::TestName::TestBody() + +// Note: this won't work correctly if the trailing arguments are macros. +#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_( \ + SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ + GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define REGISTER_TYPED_TEST_CASE_P \ + static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \ + ""); \ + REGISTER_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ + static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ + "test-suit-prefix must not be empty"); \ + static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestSuite< \ + SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ + ::testing::internal::GenerateTypeList::type>:: \ + Register(GTEST_STRINGIFY_(Prefix), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \ + GTEST_STRINGIFY_(SuiteName), \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::GenerateTypeList::type>()) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TYPED_TEST_CASE_P \ + static_assert( \ + ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \ + INSTANTIATE_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ diff --git a/gtestsuite/inc/gtest/gtest.h b/gtestsuite/inc/gtest/gtest.h new file mode 100644 index 000000000..7a5d057c4 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest.h @@ -0,0 +1,2495 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest-matchers.h" +#include "gtest/gtest-message.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest_prod.h" +#include "gtest/gtest-test-part.h" +#include "gtest/gtest-typed-test.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4805) +# pragma warning(disable:4100) +#endif + + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag controls whether the test runner should continue execution past +// first failure. +GTEST_DECLARE_bool_(fail_fast); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints only test failures. +GTEST_DECLARE_bool_(brief); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); +std::set* GetIgnoredParameterizedTestSuites(); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestSuite; + +// Old API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TestCase = TestSuite; +#endif +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +// C4800 is a level 3 warning in Visual Studio 2015 and earlier. +// This warning is not emitted in Visual Studio 2017. +// This warning is off by default starting in Visual Studio 2019 but can be +// enabled with command-line options. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename std::enable_if< + !std::is_convertible::value>::type* + /*enabler*/ + = nullptr) + : success_(success) {} + +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true if and only if the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != nullptr ? message_->c_str() : ""; + } + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == nullptr) message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + std::unique_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +// Includes the auto-generated header that implements a family of generic +// predicate assertion macros. This include comes late because it relies on +// APIs declared above. +#include "gtest/gtest_pred_impl.h" + +namespace testing { + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestSuites, and +// each TestSuite contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::SetUpTestSuite() before running the first + // test in test suite Foo. Hence a sub-class can define its own + // SetUpTestSuite() method to shadow the one defined in the super + // class. + static void SetUpTestSuite() {} + + // Tears down the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::TearDownTestSuite() after running the last + // test in test suite Foo. Hence a sub-class can define its own + // TearDownTestSuite() method to shadow the one defined in the super + // class. + static void TearDownTestSuite() {} + + // Legacy API is deprecated but still available. Use SetUpTestSuite and + // TearDownTestSuite instead. +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + static void TearDownTestCase() {} + static void SetUpTestCase() {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns true if and only if the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true if and only if the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true if and only if the current test was skipped. + static bool IsSkipped(); + + // Returns true if and only if the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test suite, or for the entire + // invocation of the test program when used outside of the context of a + // test suite. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestSuite or TearDownTestSuite are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true if and only if the current test has the same fixture class + // as the first test in the current test suite. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const std::unique_ptr gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true if and only if the test passed (i.e. no test part failed). + bool Passed() const { return !Skipped() && !Failed(); } + + // Returns true if and only if the test was skipped. + bool Skipped() const; + + // Returns true if and only if the test failed. + bool Failed() const; + + // Returns true if and only if the test fatally failed. + bool HasFatalFailure() const; + + // Returns true if and only if the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test case start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestSuite; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the start time. + void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testsuite tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properties_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test suite name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test suite name. + const char* test_suite_name() const { return test_suite_name_.c_str(); } + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const char* test_case_name() const { return test_suite_name(); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != nullptr) return value_param_->c_str(); + return nullptr; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test suite Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true if and only if this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestSuite; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, internal::CodeLocation code_location, + internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_suite_name, const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Skip and records the test result for this object. + void Skip(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_suite_name_; // test suite name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const std::unique_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True if and only if this test should run + bool is_disabled_; // True if and only if this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test suite, which consists of a vector of TestInfos. +// +// TestSuite is not copyable. +class GTEST_API_ TestSuite { + public: + // Creates a TestSuite with the given name. + // + // TestSuite does NOT have a default constructor. Always use this + // constructor to create a TestSuite object. + // + // Arguments: + // + // name: name of the test suite + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite(const char* name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + + // Destructor of TestSuite. + virtual ~TestSuite(); + + // Gets the name of the TestSuite. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test suite. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns true if any test in this test suite should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test suite. + int successful_test_count() const; + + // Gets the number of skipped tests in this test suite. + int skipped_test_count() const; + + // Gets the number of failed tests in this test suite. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test suite. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test suite that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test suite. + int total_test_count() const; + + // Returns true if and only if the test suite passed. + bool Passed() const { return !Failed(); } + + // Returns true if and only if the test suite failed. + bool Failed() const { + return failed_test_count() > 0 || ad_hoc_test_result().Failed(); + } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test suite start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestSuite and TearDownTestSuite. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestSuite. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestSuite. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test suite. Will delete the TestInfo upon + // destruction of the TestSuite object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test suite. + void ClearResult(); + + // Clears the results of all tests in the given test suite. + static void ClearTestSuiteResult(TestSuite* test_suite) { + test_suite->ClearResult(); + } + + // Runs every test in this TestSuite. + void Run(); + + // Skips the execution of tests under this TestSuite + void Skip(); + + // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed + // for catching exceptions thrown from SetUpTestSuite(). + void RunSetUpTestSuite() { + if (set_up_tc_ != nullptr) { + (*set_up_tc_)(); + } + } + + // Runs TearDownTestSuite() for this TestSuite. This wrapper is + // needed for catching exceptions thrown from TearDownTestSuite(). + void RunTearDownTestSuite() { + if (tear_down_tc_ != nullptr) { + (*tear_down_tc_)(); + } + } + + // Returns true if and only if test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true if and only if test skipped. + static bool TestSkipped(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Skipped(); + } + + // Returns true if and only if test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true if and only if the test is disabled and will be reported in + // the XML report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true if and only if test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if and only if this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test suite. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test suite. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test suite. + internal::SetUpTestSuiteFunc set_up_tc_; + // Pointer to the function that tears down the test suite. + internal::TearDownTestSuiteFunc tear_down_tc_; + // True if and only if any test in this test suite should run. + bool should_run_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestSuite and + // TearDownTestSuite. + TestResult ad_hoc_test_result_; + + // We disallow copying TestSuites. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestSuite); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test suite starts. + virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {} + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test suite ends. + virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} + void OnTestEnd(const TestInfo& /*test_info*/) override {} + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestSuite; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestSuites. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestSuite object for the test that's currently running, + // or NULL if no test is running. + const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_); + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_); +#endif + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + int successful_test_case_count() const; + int failed_test_case_count() const; + int total_test_case_count() const; + int test_case_to_run_count() const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const; + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test suites. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked + // from SetUpTestSuite or TearDownTestSuite, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableTestSuite(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend std::set* internal::GetIgnoredParameterizedTestSuites(); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleTest(); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + return EqFailure(lhs_expression, + rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), + false); +} + +// This block of code defines operator==/!= +// to block lexical scope lookup. +// It prevents using invalid operator==/!= defined at namespace scope. +struct faketype {}; +inline bool operator==(faketype, faketype) { return true; } +inline bool operator!=(faketype, faketype) { return false; } + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +class EqHelper { + public: + // This templatized version is for the general case. + template < + typename T1, typename T2, + // Disable this overload for cases where one argument is a pointer + // and the other is the null pointer constant. + typename std::enable_if::value || + !std::is_pointer::value>::type* = nullptr> + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, + BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + template + static AssertionResult Compare( + const char* lhs_expression, const char* rhs_expression, + // Handle cases where '0' is used as a null pointer literal. + std::nullptr_t /* lhs */, T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, static_cast(nullptr), + rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\ + }\ +} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, + RawType rhs_value) { + const FloatingPoint lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << rhs_value; + + return EqFailure(lhs_expression, + rhs_expression, + StringStreamToString(&lhs_ss), + StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// ~FooTest() override { +// // Can use GetParam() here. +// } +// void SetUp() override { +// // Can use GetParam() here. +// } +// void TearDown override { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. + static const ParamType& GetParam() { + GTEST_CHECK_(parameter_ != nullptr) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = nullptr; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +// Macros for indicating success/failure in test code. + +// Skips test in runtime. +// Skipping test aborts current function. +// Skipped tests are neither successful nor failed. +#define GTEST_SKIP() GTEST_SKIP_("") + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Like GTEST_FAIL(), but at the given source file location. +#define GTEST_FAIL_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define GTEST_EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define GTEST_ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Define these macros to 1 to omit the definition of the corresponding +// EXPECT or ASSERT, which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_EXPECT_TRUE +#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_EXPECT_FALSE +#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_TRUE +#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_FALSE +#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) +#endif + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles if and only if type1 and type2 +// are the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +constexpr bool StaticAssertTypeEq() noexcept { + static_assert(std::is_same::value, "T1 and T2 are not the same type"); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test suite, and the second +// parameter is the name of the test within the test suite. +// +// The convention is to end the test suite name with "Test". For +// example, a test suite for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ + ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test suite name. The second parameter is the +// name of the test within the test suite. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } +// +// GOOGLETEST_CM0011 DO NOT DELETE +#if !GTEST_DONT_DEFINE_TEST +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) +#endif // !GTEST_DONT_DEFINE_TEST + +// Returns a path to temporary directory. +// Tries to determine an appropriate directory for the platform. +GTEST_API_ std::string TempDir(); + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +// Dynamically registers a test with the framework. +// +// This is an advanced API only to be used when the `TEST` macros are +// insufficient. The macros should be preferred when possible, as they avoid +// most of the complexity of calling this function. +// +// The `factory` argument is a factory callable (move-constructible) object or +// function pointer that creates a new instance of the Test object. It +// handles ownership to the caller. The signature of the callable is +// `Fixture*()`, where `Fixture` is the test fixture class for the test. All +// tests registered with the same `test_suite_name` must return the same +// fixture type. This is checked at runtime. +// +// The framework will infer the fixture class from the factory and will call +// the `SetUpTestSuite` and `TearDownTestSuite` for it. +// +// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is +// undefined. +// +// Use case example: +// +// class MyFixture : public ::testing::Test { +// public: +// // All of these optional, just like in regular macro usage. +// static void SetUpTestSuite() { ... } +// static void TearDownTestSuite() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } +// }; +// +// class MyTest : public MyFixture { +// public: +// explicit MyTest(int data) : data_(data) {} +// void TestBody() override { ... } +// +// private: +// int data_; +// }; +// +// void RegisterMyTests(const std::vector& values) { +// for (int v : values) { +// ::testing::RegisterTest( +// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, +// std::to_string(v).c_str(), +// __FILE__, __LINE__, +// // Important to use the fixture type as the return type here. +// [=]() -> MyFixture* { return new MyTest(v); }); +// } +// } +// ... +// int main(int argc, char** argv) { +// std::vector values_to_test = LoadValuesFromConfig(); +// RegisterMyTests(values_to_test); +// ... +// return RUN_ALL_TESTS(); +// } +// +template +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory) { + using TestT = typename std::remove_pointer::type; + + class FactoryImpl : public internal::TestFactoryBase { + public: + explicit FactoryImpl(Factory f) : factory_(std::move(f)) {} + Test* CreateTest() override { return factory_(); } + + private: + Factory factory_; + }; + + return internal::MakeAndRegisterTestInfo( + test_suite_name, test_name, type_param, value_param, + internal::CodeLocation(file, line), internal::GetTypeId(), + internal::SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + internal::SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + new FactoryImpl{std::move(factory)}); +} + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/gtestsuite/inc/gtest/gtest_pred_impl.h b/gtestsuite/inc/gtest/gtest_pred_impl.h new file mode 100644 index 000000000..5029a9bb0 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest_pred_impl.h @@ -0,0 +1,359 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 01/02/2019 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +#include "gtest/gtest.h" + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ", " << e5 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n" + << e5 << " evaluates to " << ::testing::PrintToString(v5); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/gtestsuite/inc/gtest/gtest_prod.h b/gtestsuite/inc/gtest/gtest_prod.h new file mode 100644 index 000000000..38b9d85a5 --- /dev/null +++ b/gtestsuite/inc/gtest/gtest_prod.h @@ -0,0 +1,61 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// Google C++ Testing and Mocking Framework definitions useful in production code. +// GOOGLETEST_CM0003 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/gtestsuite/inc/gtest/internal/custom/README.md b/gtestsuite/inc/gtest/internal/custom/README.md new file mode 100644 index 000000000..ff391fb4e --- /dev/null +++ b/gtestsuite/inc/gtest/internal/custom/README.md @@ -0,0 +1,56 @@ +# Customization Points + +The custom directory is an injection point for custom user configurations. + +## Header `gtest.h` + +### The following macros can be defined: + +* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of + `OsStackTraceGetterInterface`. +* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See + `testing::TempDir` for semantics and signature. + +## Header `gtest-port.h` + +The following macros can be defined: + +### Flag related macros: + +* `GTEST_FLAG(flag_name)` +* `GTEST_USE_OWN_FLAGFILE_FLAG_` - Define to 0 when the system provides its + own flagfile flag parsing. +* `GTEST_DECLARE_bool_(name)` +* `GTEST_DECLARE_int32_(name)` +* `GTEST_DECLARE_string_(name)` +* `GTEST_DEFINE_bool_(name, default_val, doc)` +* `GTEST_DEFINE_int32_(name, default_val, doc)` +* `GTEST_DEFINE_string_(name, default_val, doc)` + +### Logging: + +* `GTEST_LOG_(severity)` +* `GTEST_CHECK_(condition)` +* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too. + +### Threading: + +* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided. +* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal` + are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)` + and `GTEST_DEFINE_STATIC_MUTEX_(mutex)` +* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)` +* `GTEST_LOCK_EXCLUDED_(locks)` + +### Underlying library support features + +* `GTEST_HAS_CXXABI_H_` + +### Exporting API symbols: + +* `GTEST_API_` - Specifier for exported symbols. + +## Header `gtest-printers.h` + +* See documentation at `gtest/gtest-printers.h` for details on how to define a + custom printer. diff --git a/gtestsuite/inc/gtest/internal/custom/gtest-port.h b/gtestsuite/inc/gtest/internal/custom/gtest-port.h new file mode 100644 index 000000000..db02881c0 --- /dev/null +++ b/gtestsuite/inc/gtest/internal/custom/gtest-port.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ diff --git a/gtestsuite/inc/gtest/internal/custom/gtest-printers.h b/gtestsuite/inc/gtest/internal/custom/gtest-printers.h new file mode 100644 index 000000000..b9495d837 --- /dev/null +++ b/gtestsuite/inc/gtest/internal/custom/gtest-printers.h @@ -0,0 +1,42 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ diff --git a/gtestsuite/inc/gtest/internal/custom/gtest.h b/gtestsuite/inc/gtest/internal/custom/gtest.h new file mode 100644 index 000000000..afaaf17ba --- /dev/null +++ b/gtestsuite/inc/gtest/internal/custom/gtest.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-death-test-internal.h b/gtestsuite/inc/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 000000000..490296dfa --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,304 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include "gtest/gtest-matchers.h" +#include "gtest/internal/gtest-internal.h" + +#include +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, + Matcher matcher, const char* file, + int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test) override; +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher MakeDeathTestMatcher( + Matcher matcher) { + return matcher; +} + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create( \ + #statement, \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != nullptr) { \ + std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \ + : fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-filepath.h b/gtestsuite/inc/gtest/internal/gtest-filepath.h new file mode 100644 index 000000000..0c033abc3 --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-filepath.h @@ -0,0 +1,211 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true if and only if the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurrence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-internal.h b/gtestsuite/inc/gtest/internal/gtest-internal.h new file mode 100644 index 000000000..f8cbdbd81 --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-internal.h @@ -0,0 +1,1560 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#include "gtest/internal/gtest-port.h" + +#if GTEST_OS_LINUX +# include +# include +# include +# include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-type-util.h" + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +// Stringifies its argument. +// Work around a bug in visual studio which doesn't accept code like this: +// +// #define GTEST_STRINGIFY_(name) #name +// #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ... +// MACRO(, x, y) +// +// Complaining about the argument to GTEST_STRINGIFY_ being empty. +// This is allowed by the spec. +#define GTEST_STRINGIFY_HELPER_(name, ...) #name +#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, ) + +namespace proto2 { +class MessageLite; +} + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test suites. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// An IgnoredValue object can be implicitly constructed from ANY value. +class IgnoredValue { + struct Sink {}; + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + // Disable the conversion if T already has a magical conversion operator. + // Otherwise we get ambiguity. + template ::value, + int>::type = 0> + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \ +/* an exported class was derived from a class that was not exported */) + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); + +} // namespace edit_distance + +// Calculate the diff between 'left' and 'right' and return it in unified diff +// format. +// If not null, stores in 'total_line_count' the total number of lines found +// in left + right. +GTEST_API_ std::string DiffStrings(const std::string& left, + const std::string& right, + size_t* total_line_count); + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const uint32_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true if and only if this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true if and only if this number is at most kMaxUlps ULP's away + // from rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test suite, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + Test* CreateTest() override { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestSuite() and TearDownTestSuite() functions. +using SetUpTestSuiteFunc = void (*)(); +using TearDownTestSuiteFunc = void (*)(); + +struct CodeLocation { + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} + + std::string file; + int line; +}; + +// Helper to identify which setup function for TestCase / TestSuite to call. +// Only one function is allowed, either TestCase or TestSute but not both. + +// Utility functions to help SuiteApiResolver +using SetUpTearDownSuiteFuncType = void (*)(); + +inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull( + SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) { + return a == def ? nullptr : a; +} + +template +// Note that SuiteApiResolver inherits from T because +// SetUpTestSuite()/TearDownTestSuite() could be protected. Ths way +// SuiteApiResolver can access them. +struct SuiteApiResolver : T { + // testing::Test is only forward declared at this point. So we make it a + // dependend class for the compiler to be OK with it. + using Test = + typename std::conditional::type; + + static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both SetUpTestSuite and SetUpTestCase, please " + "make sure there is only one present at " + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::SetUpTestSuite; +#endif + } + + static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both TearDownTestSuite and TearDownTestCase," + " please make sure there is only one present at" + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::TearDownTestSuite; +#endif + } +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// State of the definition of a type-parameterized test suite. +class GTEST_API_ TypedTestSuitePState { + public: + TypedTestSuitePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test suite hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, + "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.insert( + ::std::make_pair(test_name, CodeLocation(file, line))); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames(const char* test_suite_name, + const char* file, int line, + const char* registered_tests); + + private: + typedef ::std::map RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TypedTestCasePState = TypedTestSuitePState; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == nullptr) { + return nullptr; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == nullptr ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest); + +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template +struct NameGeneratorSelector { + typedef Provided type; +}; + +template +void GenerateNamesRecursively(internal::None, std::vector*, int) {} + +template +void GenerateNamesRecursively(Types, std::vector* result, int i) { + result->push_back(NameGenerator::template GetName(i)); + GenerateNamesRecursively(typename Types::Tail(), result, + i + 1); +} + +template +std::vector GenerateNames() { + std::vector result; + GenerateNamesRecursively(Types(), &result, 0); + return result; +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const CodeLocation& code_location, + const char* case_name, const char* test_names, int index, + const std::vector& type_names = + GenerateNames()) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[static_cast(index)]) + .c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName().c_str(), + nullptr, // No value parameter. + code_location, GetTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite( + code_location.file.c_str(), code_location.line), + SuiteApiResolver::GetTearDownCaseOrSuite( + code_location.file.c_str(), code_location.line), + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest::Register(prefix, + code_location, + case_name, + test_names, + index + 1, + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location); +GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation( + const char* case_name); + +// TypeParameterizedTestSuite::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestSuitePState* state, const char* case_name, + const char* test_names, + const std::vector& type_names = + GenerateNames()) { + RegisterTypeParameterizedTestSuiteInstantiation(case_name); + std::string test_name = StripTrailingSpaces( + GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), + code_location.line).c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, test_location, case_name, test_names, 0, type_names); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestSuite::Register(prefix, code_location, + state, case_name, + SkipComma(test_names), + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestSuitePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// Helper for declaring std::string within 'if' statement +// in pre C++17 build environment. +struct TrueWithString { + TrueWithString() = default; + explicit TrueWithString(const char* str) : value(str) {} + explicit TrueWithString(const std::string& str) : value(str) {} + explicit operator bool() const { return true; } + std::string value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const uint32_t kMaxRange = 1u << 31; + + explicit Random(uint32_t seed) : state_(seed) {} + + void Reseed(uint32_t seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + uint32_t Generate(uint32_t range); + + private: + uint32_t state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + typename std::remove_const::type>::type + +// HasDebugStringAndShortDebugString::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. +template +class HasDebugStringAndShortDebugString { + private: + template + static auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().DebugString())>::type; + template + static std::false_type CheckDebugString(...); + + template + static auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().ShortDebugString())>::type; + template + static std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +template +constexpr bool HasDebugStringAndShortDebugString::value; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(nullptr, nullptr)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template (0)) == sizeof(IsContainer)> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public std::false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl { + using value_type = decltype(*std::declval()); + using type = + std::is_same::type>::type, + C>; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + static_assert(!std::is_const::value, "Type must not be const"); + static_assert(!std::is_reference::value, + "Type must not be a reference"); + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); +}; + +// Backport of std::index_sequence. +template +struct IndexSequence { + using type = IndexSequence; +}; + +// Double the IndexSequence, and one if plus_one is true. +template +struct DoubleSequence; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; + +// Backport of std::make_index_sequence. +// It uses O(ln(N)) instantiation depth. +template +struct MakeIndexSequenceImpl + : DoubleSequence::type, + N / 2>::type {}; + +template <> +struct MakeIndexSequenceImpl<0> : IndexSequence<> {}; + +template +using MakeIndexSequence = typename MakeIndexSequenceImpl::type; + +template +using IndexSequenceFor = typename MakeIndexSequence::type; + +template +struct Ignore { + Ignore(...); // NOLINT +}; + +template +struct ElemFromListImpl; +template +struct ElemFromListImpl> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; + +template +struct ElemFromList { + using type = + decltype(ElemFromListImpl::type>::Apply( + static_cast(nullptr)...)); +}; + +struct FlatTupleConstructTag {}; + +template +class FlatTuple; + +template +struct FlatTupleElemBase; + +template +struct FlatTupleElemBase, I> { + using value_type = typename ElemFromList::type; + FlatTupleElemBase() = default; + template + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward(t)) {} + value_type value; +}; + +template +struct FlatTupleBase; + +template +struct FlatTupleBase, IndexSequence> + : FlatTupleElemBase, Idx>... { + using Indices = IndexSequence; + FlatTupleBase() = default; + template + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase, Idx>(FlatTupleConstructTag{}, + std::forward(args))... {} + + template + const typename ElemFromList::type& Get() const { + return FlatTupleElemBase, I>::value; + } + + template + typename ElemFromList::type& Get() { + return FlatTupleElemBase, I>::value; + } + + template + auto Apply(F&& f) -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } + + template + auto Apply(F&& f) const -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } +}; + +// Analog to std::tuple but with different tradeoffs. +// This class minimizes the template instantiation depth, thus allowing more +// elements than std::tuple would. std::tuple has been seen to require an +// instantiation depth of more than 10x the number of elements in some +// implementations. +// FlatTuple and ElemFromList are not recursive and have a fixed depth +// regardless of T... +// MakeIndexSequence, on the other hand, it is recursive but with an +// instantiation depth of O(ln(N)). +template +class FlatTuple + : private FlatTupleBase, + typename MakeIndexSequence::type> { + using Indices = typename FlatTupleBase< + FlatTuple, typename MakeIndexSequence::type>::Indices; + + public: + FlatTuple() = default; + template + explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args) + : FlatTuple::FlatTupleBase(tag, std::forward(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; +}; + +// Utility functions to be called with static_assert to induce deprecation +// warnings. +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TEST_SUITE_P") +constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE_P is deprecated, please use " + "TYPED_TEST_SUITE_P") +constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE is deprecated, please use " + "TYPED_TEST_SUITE") +constexpr bool TypedTestCaseIsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " + "REGISTER_TYPED_TEST_SUITE_P") +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TYPED_TEST_SUITE_P") +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + +} // namespace internal +} // namespace testing + +namespace std { +// Some standard library implementations use `struct tuple_size` and some use +// `class tuple_size`. Clang warns about the mismatch. +// https://reviews.llvm.org/D55466 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +struct tuple_size> + : std::integral_constant {}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace std + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +#define GTEST_SKIP_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) + +// Suppress MSVC warning 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +// NOTE: The "else" is important to keep this expansion to prevent a top-level +// "else" from attaching to our "if". +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } else /* NOLINT */ \ + static_assert(true, "") // User must have a semicolon after expansion. + +#if GTEST_HAS_EXCEPTIONS + +namespace testing { +namespace internal { + +class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } +}; + +} // namespace internal +} // namespace testing + +#if GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e)) + +#else // GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) \ + std::string { "an std::exception-derived error" } + +#endif // GTEST_HAS_RTTI + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (typename std::conditional< \ + std::is_same::type>::type, \ + std::exception>::value, \ + const ::testing::internal::NeverThrown&, const std::exception&>::type \ + e) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#if GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (std::exception const& e) { \ + gtest_msg.value = "it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (...) { \ + gtest_msg.value = "it throws."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail(("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: " + gtest_msg.value).c_str()) + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// representation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + \ + private: \ + void TestBody() override; \ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-param-util.h b/gtestsuite/inc/gtest/internal/gtest-param-util.h new file mode 100644 index 000000000..c2ef6e312 --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-param-util.h @@ -0,0 +1,947 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Type and function utilities for implementing parameterized tests. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" + +namespace testing { +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) : + param(a_param), + index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template + std::string operator()(const TestParamInfo& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// Utility Functions + +// Outputs a message explaining invalid registration of different +// fixture class for the same test suite. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location); + +template class ParamGeneratorInterface; +template class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + std::unique_ptr > impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + std::shared_ptr > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + ~RangeGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, begin_, 0, step_); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + value_ = static_cast(value_ + step_); + index_++; + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const T* Current() const override { return &value_; } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast(i + step)) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + ~ValuesInIteratorRangeGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, container_.begin()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++iterator_; + value_.reset(); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + const T* Current() const override { + if (value_.get() == nullptr) value_.reset(new T(*iterator_)); + return value_.get(); + } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of std::unique_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable std::unique_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template +std::string DefaultParamName(const TestParamInfo& info) { + Message name_stream; + name_stream << info.index; + return name_stream.GetString(); +} + +template +void TestNotEmpty() { + static_assert(sizeof(T) == 0, "Empty arguments are not allowed."); +} +template +void TestNotEmpty(const T&) {} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + Test* CreateTest() override { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestSuiteInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + using ParamType = typename TestSuite::ParamType; + + TestMetaFactory() {} + + TestFactoryBase* CreateTestFactory(ParamType parameter) override { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfoBase is a generic interface +// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds +// a collection of pointers to the ParameterizedTestSuiteInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestSuiteInfoBase { + public: + virtual ~ParameterizedTestSuiteInfoBase() {} + + // Base part of test suite name for display purposes. + virtual const std::string& GetTestSuiteName() const = 0; + // Test suite id to verify identity. + virtual TypeId GetTestSuiteTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test suite right before running them in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestSuiteInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Report a the name of a test_suit as safe to ignore +// as the side effect of construction of this type. +struct GTEST_API_ MarkAsIgnored { + explicit MarkAsIgnored(const char* test_suite); +}; + +GTEST_API_ void InsertSyntheticTestCase(const std::string& name, + CodeLocation location, bool has_test_p); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test suite and generators +// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that +// test suite. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestSuiteInstantiation(). + using ParamType = typename TestSuite::ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + using ParamNameGeneratorFunc = std::string(const TestParamInfo&); + + explicit ParameterizedTestSuiteInfo(const char* name, + CodeLocation code_location) + : test_suite_name_(name), code_location_(code_location) {} + + // Test suite base name for display purposes. + const std::string& GetTestSuiteName() const override { + return test_suite_name_; + } + // Test suite id to verify identity. + TypeId GetTestSuiteTypeId() const override { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_suite_name is the base name of the test suite (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test suite base name and DoBar is test base name. + void AddTestPattern(const char* test_suite_name, const char* test_base_name, + TestMetaFactoryBase* meta_factory, + CodeLocation code_location) { + tests_.push_back(std::shared_ptr(new TestInfo( + test_suite_name, test_base_name, meta_factory, code_location))); + } + // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestSuiteInstantiation(const std::string& instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.push_back( + InstantiationInfo(instantiation_name, func, name_func, file, line)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test suite + // right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more than once. + void RegisterTests() override { + bool generated_instantiations = false; + + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + std::shared_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const std::string& instantiation_name = gen_it->name; + ParamGenerator generator((*gen_it->generator)()); + ParamNameGeneratorFunc* name_func = gen_it->name_func; + const char* file = gen_it->file; + int line = gen_it->line; + + std::string test_suite_name; + if ( !instantiation_name.empty() ) + test_suite_name = instantiation_name + "/"; + test_suite_name += test_info->test_suite_base_name; + + size_t i = 0; + std::set test_param_names; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + generated_instantiations = true; + + Message test_name_stream; + + std::string param_name = name_func( + TestParamInfo(*param_it, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid, in " << file + << " line " << line << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name + << "', in " << file << " line " << line << std::endl; + + test_param_names.insert(param_name); + + if (!test_info->test_base_name.empty()) { + test_name_stream << test_info->test_base_name << "/"; + } + test_name_stream << param_name; + MakeAndRegisterTestInfo( + test_suite_name.c_str(), test_name_stream.GetString().c_str(), + nullptr, // No type parameter. + PrintToString(*param_it).c_str(), test_info->code_location, + GetTestSuiteTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_, + !tests_.empty()); + } + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory, + CodeLocation a_code_location) + : test_suite_base_name(a_test_suite_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory), + code_location(a_code_location) {} + + const std::string test_suite_base_name; + const std::string test_base_name; + const std::unique_ptr > test_meta_factory; + const CodeLocation code_location; + }; + using TestInfoContainer = ::std::vector >; + // Records data received from INSTANTIATE_TEST_SUITE_P macros: + // + struct InstantiationInfo { + InstantiationInfo(const std::string &name_in, + GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, + const char* file_in, + int line_in) + : name(name_in), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) + return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!IsAlNum(name[index]) && name[index] != '_') + return false; + } + + return true; + } + + const std::string test_suite_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo); +}; // class ParameterizedTestSuiteInfo + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +template +using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteRegistry contains a map of +// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P +// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding +// ParameterizedTestSuiteInfo descriptors. +class ParameterizedTestSuiteRegistry { + public: + ParameterizedTestSuiteRegistry() {} + ~ParameterizedTestSuiteRegistry() { + for (auto& test_suite_info : test_suite_infos_) { + delete test_suite_info; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test suite. + template + ParameterizedTestSuiteInfo* GetTestSuitePatternHolder( + const char* test_suite_name, CodeLocation code_location) { + ParameterizedTestSuiteInfo* typed_test_info = nullptr; + for (auto& test_suite_info : test_suite_infos_) { + if (test_suite_info->GetTestSuiteName() == test_suite_name) { + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name, code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestSuiteInfo >(test_suite_info); + } + break; + } + } + if (typed_test_info == nullptr) { + typed_test_info = new ParameterizedTestSuiteInfo( + test_suite_name, code_location); + test_suite_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (auto& test_suite_info : test_suite_infos_) { + test_suite_info->RegisterTests(); + } + } +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder(test_case_name, code_location); + } + +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + private: + using TestSuiteInfoContainer = ::std::vector; + + TestSuiteInfoContainer test_suite_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry); +}; + +// Keep track of what type-parameterized test suite are defined and +// where as well as which are intatiated. This allows susequently +// identifying suits that are defined but never used. +class TypeParameterizedTestSuiteRegistry { + public: + // Add a suite definition + void RegisterTestSuite(const char* test_suite_name, + CodeLocation code_location); + + // Add an instantiation of a suit. + void RegisterInstantiation(const char* test_suite_name); + + // For each suit repored as defined but not reported as instantiation, + // emit a test that reports that fact (configurably, as an error). + void CheckForInstantiations(); + + private: + struct TypeParameterizedTestSuiteInfo { + explicit TypeParameterizedTestSuiteInfo(CodeLocation c) + : code_location(c), instantiated(false) {} + + CodeLocation code_location; + bool instantiated; + }; + + std::map suites_; +}; + +} // namespace internal + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { +// Used in the Values() function to provide polymorphic capabilities. + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + +template +class ValueArray { + public: + explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {} + + template + operator ParamGenerator() const { // NOLINT + return ValuesIn(MakeVector(MakeIndexSequence())); + } + + private: + template + std::vector MakeVector(IndexSequence) const { + return std::vector{static_cast(v_.template Get())...}; + } + + FlatTuple v_; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template +class CartesianProductGenerator + : public ParamGeneratorInterface<::std::tuple> { + public: + typedef ::std::tuple ParamType; + + CartesianProductGenerator(const std::tuple...>& g) + : generators_(g) {} + ~CartesianProductGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generators_, false); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generators_, true); + } + + private: + template + class IteratorImpl; + template + class IteratorImpl> + : public ParamIteratorInterface { + public: + IteratorImpl(const ParamGeneratorInterface* base, + const std::tuple...>& generators, bool is_end) + : base_(base), + begin_(std::get(generators).begin()...), + end_(std::get(generators).end()...), + current_(is_end ? end_ : begin_) { + ComputeCurrentValue(); + } + ~IteratorImpl() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + void Advance() override { + assert(!AtEnd()); + // Advance the last iterator. + ++std::get(current_); + // if that reaches end, propagate that up. + AdvanceIfEnd(); + ComputeCurrentValue(); + } + ParamIteratorInterface* Clone() const override { + return new IteratorImpl(*this); + } + + const ParamType* Current() const override { return current_value_.get(); } + + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const IteratorImpl* typed_other = + CheckedDowncastToActualType(&other); + + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + if (AtEnd() && typed_other->AtEnd()) return true; + + bool same = true; + bool dummy[] = { + (same = same && std::get(current_) == + std::get(typed_other->current_))...}; + (void)dummy; + return same; + } + + private: + template + void AdvanceIfEnd() { + if (std::get(current_) != std::get(end_)) return; + + bool last = ThisI == 0; + if (last) { + // We are done. Nothing else to propagate. + return; + } + + constexpr size_t NextI = ThisI - (ThisI != 0); + std::get(current_) = std::get(begin_); + ++std::get(current_); + AdvanceIfEnd(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = std::make_shared(*std::get(current_)...); + } + bool AtEnd() const { + bool at_end = false; + bool dummy[] = { + (at_end = at_end || std::get(current_) == std::get(end_))...}; + (void)dummy; + return at_end; + } + + const ParamGeneratorInterface* const base_; + std::tuple::iterator...> begin_; + std::tuple::iterator...> end_; + std::tuple::iterator...> current_; + std::shared_ptr current_value_; + }; + + using Iterator = IteratorImpl::type>; + + std::tuple...> generators_; +}; + +template +class CartesianProductHolder { + public: + CartesianProductHolder(const Gen&... g) : generators_(g...) {} + template + operator ParamGenerator<::std::tuple>() const { + return ParamGenerator<::std::tuple>( + new CartesianProductGenerator(generators_)); + } + + private: + std::tuple generators_; +}; + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-port-arch.h b/gtestsuite/inc/gtest/internal/gtest-port-arch.h new file mode 100644 index 000000000..dd845915e --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-port-arch.h @@ -0,0 +1,114 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +# elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +# define GTEST_OS_WINDOWS_MINGW 1 +# define GTEST_OS_WINDOWS 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GTEST_OS_WINDOWS_DESKTOP 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +# define GTEST_OS_WINDOWS_PHONE 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +# define GTEST_OS_WINDOWS_RT 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +# define GTEST_OS_WINDOWS_PHONE 1 +# define GTEST_OS_WINDOWS_TV_TITLE 1 +# else + // WINAPI_FAMILY defined but no known partition matched. + // Default to desktop. +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __OS2__ +# define GTEST_OS_OS2 1 +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# include +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# endif +#elif defined __DragonFly__ +# define GTEST_OS_DRAGONFLY 1 +#elif defined __FreeBSD__ +# define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +# define GTEST_OS_FUCHSIA 1 +#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__) +# define GTEST_OS_GNU_KFREEBSD 1 +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +# define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#elif defined(__HAIKU__) +#define GTEST_OS_HAIKU 1 +#elif defined ESP8266 +#define GTEST_OS_ESP8266 1 +#elif defined ESP32 +#define GTEST_OS_ESP32 1 +#elif defined(__XTENSA__) +#define GTEST_OS_XTENSA 1 +#endif // __CYGWIN__ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-port.h b/gtestsuite/inc/gtest/internal/gtest-port.h new file mode 100644 index 000000000..0953a781c --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-port.h @@ -0,0 +1,2389 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_DRAGONFLY - DragonFlyBSD +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD +// GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_OS2 - OS/2 +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #if +// which controls that code. For example: +// +// #if GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GOOGLETEST_CM0007 DO NOT DELETE +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables copy operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_DISALLOW_MOVE_ASSIGN_ - disables move operator=. +// GTEST_DISALLOW_MOVE_AND_ASSIGN_ - disables move ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter or +// UniversalPrinter specializations. +// GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter +// or +// UniversalPrinter +// specializations. +// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher or +// Matcher +// specializations. +// GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter or +// UniversalPrinter +// specializations. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like platforms +// GOOGLETEST_CM0008 DO NOT DELETE +// or a reduced regular exception syntax on other +// platforms, including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// TimeInMillis - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an int32_t environment variable. +// StringFromGTestEnv() - parses a string environment variable. +// +// Deprecation warnings: +// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as +// deprecated; calling a marked function +// should generate a compiler warning + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include + +#include +#include +#include +#include + +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT +#include +#include +#include // NOLINT +#include +#include // NOLINT + +#include "gtest/internal/custom/gtest-port.h" +#include "gtest/internal/gtest-port-arch.h" + +#if !defined(GTEST_DEV_EMAIL_) +# define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +# define GTEST_FLAG_PREFIX_ "gtest_" +# define GTEST_FLAG_PREFIX_DASH_ "gtest-" +# define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +# define GTEST_NAME_ "Google Test" +# define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +# define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if defined(_MSC_VER) +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) \ + __pragma(warning(disable: warnings)) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() \ + __pragma(warning(pop)) +#else +// Not all compilers are MSVC +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + _Pragma("clang diagnostic pop") +#else +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS_MOBILE +# include +# include +# endif +// In order to avoid having to include , use forward declaration +#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#elif GTEST_OS_XTENSA +#include +// Xtensa toolchains define strcasecmp in the string.h header instead of +// strings.h. string.h is already included. +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include +# include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true if and only if Google Test can use POSIX regular +// expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +#define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS && !GTEST_OS_XTENSA) +# endif +#endif + +#if GTEST_USES_PCRE +// The appropriate headers have already been included. + +#elif GTEST_HAS_POSIX_RE + +// On some platforms, needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included , which is guaranteed to define size_t through +// . +# include // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_USES_PCRE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__clang__) +// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang +// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files, +// there can be cleanups for ObjC exceptions which also need cleanups, even if +// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which +// checks for C++ exceptions starting at clang r206352, but which checked for +// cleanups prior to that. To reliably check for C++ exception availability with +// clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +#define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || GTEST_OS_XTENSA)) + +#endif // GTEST_HAS_STD_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is +// enabled. +# elif defined(__GNUC__) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#define GTEST_HAS_PTHREAD \ + (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD || \ + GTEST_OS_HAIKU) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +# include // NOLINT + +// For timespec and nanosleep, used below. +# include // NOLINT +#endif + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +# if defined(__LP64__) || \ + (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW || \ + GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU) +# define GTEST_HAS_DEATH_TEST 1 +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#elif defined(__clang__) +# if __has_attribute(unused) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +# endif +#endif +#ifndef GTEST_ATTRIBUTE_UNUSED_ +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// Use this annotation before a function that takes a printf format string. +#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC) +# if defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \ + first_to_check))) +# else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +# endif +#else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + + +// A macro to disallow copy operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type) \ + type& operator=(type const &) = delete + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \ + type(type const&) = delete; \ + type& operator=(type const&) = delete + +// A macro to disallow move operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_MOVE_ASSIGN_(type) \ + type& operator=(type &&) noexcept = delete + +// A macro to disallow move constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_MOVE_AND_ASSIGN_(type) \ + type(type&&) noexcept = delete; \ + type& operator=(type&&) noexcept = delete + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && !COMPILER_ICC + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +# define GTEST_INTENTIONAL_CONST_COND_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifndef GTEST_IS_THREADSAFE + +#define GTEST_IS_THREADSAFE \ + (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \ + GTEST_HAS_PTHREAD) + +#endif // GTEST_IS_THREADSAFE + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif +#elif __GNUC__ >= 4 || defined(__clang__) +# define GTEST_API_ __attribute__((visibility ("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +# define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +# define GTEST_HAS_CXXABI_H_ 1 +# else +# define GTEST_HAS_CXXABI_H_ 0 +# endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if defined(__clang__) +# if __has_feature(memory_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \ + __attribute__((no_sanitize_memory)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +# endif // __has_feature(memory_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif // __clang__ + +// A function level attribute to disable AddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(address_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +# endif // __has_feature(address_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif // __clang__ + +// A function level attribute to disable HWAddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(hwaddress_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ + __attribute__((no_sanitize("hwaddress"))) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +# endif // __has_feature(hwaddress_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +#endif // __clang__ + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(thread_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \ + __attribute__((no_sanitize_thread)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +# endif // __has_feature(thread_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif // __clang__ + +namespace testing { + +class Message; + +// Legacy imports for backwards compatibility. +// New code should use std:: names directly. +using std::get; +using std::make_tuple; +using std::tuple; +using std::tuple_element; +using std::tuple_size; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ is a legacy macro used to verify that a compile +// time expression is true (in new code, use static_assert instead). For +// example, you could use it to verify the size of a static array: +// +// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES, +// names_incorrect_size); +// +// The second argument to the macro must be a valid C++ identifier. If the +// expression is false, compiler will issue an error containing this identifier. +#define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg) + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines RE. + +#if GTEST_USES_PCRE +// if used, PCRE is injected by custom/gtest-port.h +#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true if and only if regular expression re + // matches the entire str. + // PartialMatch(str, re) returns true if and only if regular expression re + // matches a substring of str (including str itself). + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + const char* pattern_; + bool is_valid_; + +# if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +# else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +# endif +}; + +#endif // GTEST_USES_PCRE + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#if !defined(GTEST_LOG_) + +# define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(nullptr); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +# define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { typedef const T& type; }; +template +struct ConstRef { typedef T& type; }; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (false) { + GTEST_INTENTIONAL_CONST_COND_POP_() + const To to = nullptr; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == nullptr || dynamic_cast(f) != nullptr); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); +#endif + +#if GTEST_HAS_DOWNCAST_ + return ::down_cast(base); +#elif GTEST_HAS_RTTI + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector GetArgvs(); + +#if GTEST_HAS_DEATH_TEST + +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#if GTEST_IS_THREADSAFE +# if GTEST_HAS_PTHREAD +// Sleeps for (roughly) n milliseconds. This function is only for testing +// Google Test's own constructs. Don't use it in user tests, either +// directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, nullptr); +} +# endif // GTEST_HAS_PTHREAD + +# if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_HAS_PTHREAD +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +GTEST_API_ void SleepMilliseconds(int n); + +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true if and only if the handle is a valid handle object that can be + // closed. + bool IsCloseable() const; + + Handle handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class GTEST_API_ Notification { + public: + Notification(); + void Notify(); + void WaitForNotification(); + + private: + AutoHandle event_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; +# endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return nullptr; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() override { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr)); + finished_ = true; + } + } + + void Run() override { + if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true if and only if we know that the thread function has + // finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; +# endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +# if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase); +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) { + } + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) + : func_(func), + param_(param) { + } + virtual ~RunnableImpl() {} + virtual void Run() { + func_(param_); + } + + private: + UserThreadFunc* const func_; + const T param_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl); + }; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer(); + } + + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + std::unique_ptr default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != nullptr) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + std::unique_ptr default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +#ifdef __cpp_char8_t +inline bool IsXDigit(char8_t ch) { + return isxdigit(static_cast(ch)) != 0; +} +#endif +inline bool IsXDigit(char16_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(char32_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) + it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int DoIsATTY(int /* fd */) { return 0; } +# else +inline int DoIsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#elif GTEST_OS_ESP8266 +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +inline int IsATTY(int fd) { + // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout + // to a file on Linux), which is unexpected, so save the previous value, and + // restore it after the call. + int savedErrno = errno; + int isAttyValue = DoIsATTY(fd); + errno = savedErrno; + + return isAttyValue; +} + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ + !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + struct wchar_codecvt : public std::codecvt {}; + std::wstring_convert converter; + std::wstring wide_path = converter.from_bytes(path); + std::wstring wide_mode = converter.from_bytes(mode); + return _wfopen(wide_path.c_str(), wide_mode.c_str()); +#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + return fopen(path, mode); +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA + // We are on an embedded platform, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. + return nullptr; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != nullptr && env[0] != '\0') ? env : nullptr; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +[[noreturn]] void Abort(); +#else +[[noreturn]] inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The biggest signed integer type the compiler supports. +// +// long long is guaranteed to be at least 64-bits in C++11. +using BiggestInt = long long; // NOLINT + +// The maximum number a BiggestInt can represent. +constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits::max)(); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + using UInt = void; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + using Int = std::int32_t; + using UInt = std::uint32_t; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + using Int = std::int64_t; + using UInt = std::uint64_t; +}; + +// Integer types of known sizes. +using TimeInMillis = int64_t; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +# define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +#if !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) +# define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 +#endif // !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) + +#if !defined(GTEST_DECLARE_bool_) +# define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +// Macros for declaring flags. +# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +# define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern std::int32_t GTEST_FLAG(name) +# define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +# define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +#endif // !defined(GTEST_DECLARE_bool_) + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +# define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +# define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +GTEST_API_ bool ParseInt32(const Message& src_text, const char* str, + int32_t* value); + +// Parses a bool/int32_t/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#if !defined(GTEST_INTERNAL_DEPRECATED) + +// Internal Macro to mark an API deprecated, for googletest usage only +// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or +// GTEST_INTERNAL_DEPRECATED(message) myFunction(); Every usage of +// a deprecated entity will trigger a warning when compiled with +// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). +// For msvc /W3 option will need to be used +// Note that for 'other' compilers this macro evaluates to nothing to prevent +// compilations errors. +#if defined(_MSC_VER) +#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__GNUC__) +#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define GTEST_INTERNAL_DEPRECATED(message) +#endif + +#endif // !defined(GTEST_INTERNAL_DEPRECATED) + +#if GTEST_HAS_ABSL +// Always use absl::any for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_ANY 1 +#include "absl/types/any.h" +namespace testing { +namespace internal { +using Any = ::absl::any; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::any for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_ANY 1 +#include +namespace testing { +namespace internal { +using Any = ::std::any; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::any is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::optional for UniversalPrinter<> specializations if +// googletest is built with absl support. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include "absl/types/optional.h" +namespace testing { +namespace internal { +template +using Optional = ::absl::optional; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::optional for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include +namespace testing { +namespace internal { +template +using Optional = ::std::optional; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::optional is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::string_view for Matcher<> specializations if googletest +// is built with absl support. +# define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include "absl/strings/string_view.h" +namespace testing { +namespace internal { +using StringView = ::absl::string_view; +} // namespace internal +} // namespace testing +#else +# ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::string_view for Matcher<> +// specializations. +# define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include +namespace testing { +namespace internal { +using StringView = ::std::string_view; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::string_view is not +// supported. +# endif // __has_include() && __cplusplus >= 201703L +# endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::variant for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include "absl/types/variant.h" +namespace testing { +namespace internal { +template +using Variant = ::absl::variant; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::variant for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include +namespace testing { +namespace internal { +template +using Variant = ::std::variant; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::variant is not supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-string.h b/gtestsuite/inc/gtest/internal/gtest-string.h new file mode 100644 index 000000000..10f774f96 --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-string.h @@ -0,0 +1,175 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by gtest-internal.h. +// It should not be #included by other files. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true if and only if they have the same + // content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true if and only if they have the + // same content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true if and only if the given string ends with the given suffix, + // ignoring case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value to given width with leading zeros. + static std::string FormatIntWidthN(int value, int width); + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats an int value as "%X". + static std::string FormatHexUInt32(uint32_t value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/gtestsuite/inc/gtest/internal/gtest-type-util.h b/gtestsuite/inc/gtest/internal/gtest-type-util.h new file mode 100644 index 000000000..b87a2e2ca --- /dev/null +++ b/gtestsuite/inc/gtest/internal/gtest-type-util.h @@ -0,0 +1,183 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type utilities needed for implementing typed and type-parameterized +// tests. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + +#if GTEST_HAS_RTTI +// GetTypeName(const std::type_info&) returns a human-readable name of type T. +inline std::string GetTypeName(const std::type_info& type) { + const char* const name = type.name(); +#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +#if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +#endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +#else + return name; +#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC +} +#endif // GTEST_HAS_RTTI + +// GetTypeName() returns a human-readable name of type T if and only if +// RTTI is enabled, otherwise it returns a dummy type name. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +#if GTEST_HAS_RTTI + return GetTypeName(typeid(T)); +#else + return ""; +#endif // GTEST_HAS_RTTI +} + +// A unique type indicating an empty node +struct None {}; + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +template +struct Templates { + using Head = TemplateSel; + using Tail = Templates; +}; + +template +struct Templates { + using Head = TemplateSel; + using Tail = None; +}; + +// Tuple-like type lists +template +struct Types { + using Head = Head_; + using Tail = Types; +}; + +template +struct Types { + using Head = Head_; + using Tail = None; +}; + +// Helper metafunctions to tell apart a single type from types +// generated by ::testing::Types +template +struct ProxyTypeList { + using type = Types; +}; + +template +struct is_proxy_type_list : std::false_type {}; + +template +struct is_proxy_type_list> : std::true_type {}; + +// Generator which conditionally creates type lists. +// It recognizes if a requested type list should be created +// and prevents creating a new type list nested within another one. +template +struct GenerateTypeList { + private: + using proxy = typename std::conditional::value, T, + ProxyTypeList>::type; + + public: + using type = typename proxy::type; +}; + +} // namespace internal + +template +using Types = internal::ProxyTypeList; + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/gtestsuite/input.general b/gtestsuite/input.general new file mode 100644 index 000000000..8265c7cca --- /dev/null +++ b/gtestsuite/input.general @@ -0,0 +1,63 @@ +# ---------------------------------------------------------------------- +# +# input.general +# BLIS GtestSuite +# +# This file contains input values that control how BLIS operations are +# tested. Comments explain the purpose of each parameter as well as +# accepted values. +# + +1 # Number of repeats per experiment (best result is reported) +rc # Matrix storage scheme(s) to test: + # 'c' = col-major storage; 'g' = general stride storage; + # 'r' = row-major storage +cj # Vector storage scheme(s) to test: + # 'c' = colvec / unit stride; 'j' = colvec / non-unit stride; + # 'r' = rowvec / unit stride; 'i' = rowvec / non-unit stride +0 # Test all combinations of storage schemes? +1 # Perform all tests with alignment? + # '0' = do NOT align buffers/ldims; '1' = align buffers/ldims +0 # Randomize vectors and matrices using: + # '0' = real values on [-1,1]; + # '1' = powers of 2 in narrow precision range +32 # General stride spacing (for cases when testing general stride) +sdcz # Datatype(s) to test: + # 's' = single real; 'c' = single complex; + # 'd' = double real; 'z' = double complex +0 # Test gemm with mixed-domain operands? +0 # Test gemm with mixed-precision operands? +10 # Problem size: first to test +100 # Problem size: maximum to test +10 # Problem size: increment between experiments + # Complex level-3 implementations to test: +0 # 3mh ('1' = enable; '0' = disable) +0 # 3m1 ('1' = enable; '0' = disable) +0 # 4mh ('1' = enable; '0' = disable) +0 # 4m1b ('1' = enable; '0' = disable) +0 # 4m1a ('1' = enable; '0' = disable) +0 # 1m ('1' = enable; '0' = disable) +1 # native ('1' = enable; '0' = disable) +1 # Simulate application-level threading: + # '1' = disable / use one testsuite thread; + # 'n' = enable and use n testsuite threads +1 # Error-checking level: + # '0' = disable error checking; '1' = full error checking +i # Reaction to test failure: + # 'i' = ignore; 's' = sleep() and continue; 'a' = abort +0 # Output results in matlab/octave format? ('1' = yes; '0' = no) +0 # Output results to stdout AND files? ('1' = yes; '0' = no) +0 # api : '0' = BLIS / '1' = CBLAS / '2' = BLAS +0 # m/n/k : '0' = Values where m=n=k / '1' = Multiple combinations of m/n/k +0 # alpha/beta : '0' = Fixed value / '1' = Multiple values with combination +0 # Integer bit-exactness testing + # '0' = 's/d/c/z' datatype testing + # '1' = Integer bit-exactness testing +0 # '0' = Print only fail cases + # '1' = Print pass and fail cases +0 # Bit-Reproducibility + # '0' = Disable + # '1' = Enable +r # lpgemm memformat reorder + # 'p' = No reorder + # 'r' = reorder diff --git a/gtestsuite/input.operations b/gtestsuite/input.operations new file mode 100644 index 000000000..d7cfe5253 --- /dev/null +++ b/gtestsuite/input.operations @@ -0,0 +1,321 @@ +# -------------------------------------------------------------------------- +# +# input.operations +# BLIS GtestSuite +# +# This file contains input values that control which BLIS operations are +# tested as well as how those test runs are parameterized. We will now +# describe how each section or line type may be edited. +# +# ENABLING/DISABLING INDIVIDUAL OPERATION TESTS +# Given that an operation's section override switch is set to 1 +# (enabled), whether or not that operation will get tested is +# determined by its local switch. For example, if the level-1v section +# override is set to 1, and there is a 1 on the line marked "addv", +# then the addv operation will be tested. Similarly, a 0 would cause +# addv to not be tested. +# +# CHANGING PROBLEM SIZE/SHAPES TESTED +# The problem sizes tested by an operation are determined by the +# dimension specifiers on the line marked "dimensions: ". +# If, for example, contains two dimension labels (e.g. +# "m n"), then the line should begin with two dimension specifiers. +# Dimension specifiers of -1 cause the corresponding dimension to be +# bound to the problem size, which is determined by values set in +# input.general. Positive values cause the corresponding dimension to +# be fixed to that value and held constant. +# +# Examples of dimension specifiers (where the dimensions are m and n): +# +# -1 -1 Dimensions m and n grow with problem size (resulting in +# square matrices). +# -1 150 Dimension m grows with problem size and n is fixed at +# 150. +# -1 -2 Dimension m grows with problem size and n grows +# proportional to half the problem size. +# +# CHANGING PARAMTER COMBINATIONS TESTED +# The parameter combinations tested by an operation are determined by +# the parameter specifier characters on the line marked "parameters: +# ". If, for example, contains two +# parameter labels (e.g. "transa conjx"), then the line should contain +# two parameter specifier characters. The '?' specifier character +# serves as a wildcard--it causes all possible values of that parameter +# to be tested. A character such as 'n' or 't' causes only that value +# to be tested. +# +# Examples of parameter specifiers (where the parameters are transa +# and conjx): +# +# ?? All combinations of the transa and conjx parameters are +# tested: nn, nc, tn, tc, cn, cc, hn, hc. +# ?n conjx is fixed to "no conjugate" but transa is allowed +# to vary: nn, tn, cn, hn. +# hc Only the case where transa is "Hermitian-transpose" and +# conjx is "conjugate" is tested. +# +# Here is a full list of the parameter types used by the various BLIS +# operations along with their possible character encodings: +# +# side: l,r left, right +# uplo: l,u lower, upper +# trans: n,t,c,h no transpose, transpose, conjugate, Hermitian- +# transpose (i.e. conjugate-transpose) +# conj: n,c no conjugate, conjugate +# diag: n,u non-unit diagonal, unit diagonal +# +# -------------------------------------------------------------------------- + +# --- Utility -------------------------------------------------------------- + +0 # randv +-1 # dimensions: m + +0 # randm +-1 -1 # dimensions: m n + + +# --- Level-1v ------------------------------------------------------------- + +0 # addv +-1 # dimensions: m +? # parameters: conjx + +0 # amaxv +-1 # dimensions: m + +0 # axpbyv +-1 # dimensions: m +? # parameters: conjx + +0 # axpyv +-1 # dimensions: m +? # parameters: conjx + +0 # copyv +-1 # dimensions: m +? # parameters: conjx + +0 # dotv +-1 # dimensions: m +?? # parameters: conjx conjy + +0 # dotxv +-1 # dimensions: m +?? # parameters: conjx conjy + +0 # normfv +-1 # dimensions: m + +0 # scalv +-1 # dimensions: m +? # parameters: conjbeta + +0 # scal2v +-1 # dimensions: m +? # parameters: conjx + +0 # setv +-1 # dimensions: m + +0 # subv +-1 # dimensions: m +? # parameters: conjx + +0 # xpbyv +-1 # dimensions: m +? # parameters: conjx + + +# --- Level-1m ------------------------------------------------------------- + +0 # addm +-1 -2 # dimensions: m n +? # parameters: transa + +0 # axpym +-1 -1 # dimensions: m n +? # parameters: transa + +0 # copym +-1 -2 # dimensions: m n +? # parameters: transa + +0 # normfm +-1 -2 # dimensions: m n + +0 # scalm +-1 -2 # dimensions: m n +? # parameters: conjbeta + +0 # scal2m +-1 -2 # dimensions: m n +? # parameters: transa + +0 # setm +-1 -2 # dimensions: m n + +0 # subm +-1 -2 # dimensions: m n +? # parameters: transa + +0 # xpbym +-1 -1 # dimensions: m n +? # parameters: transa + + +# --- Level-1f kernels ----------------------------------------------------- + +0 # axpy2v +-1 # dimensions: m +?? # parameters: conjx conjy + +0 # dotaxpyv +-1 # dimensions: m +??? # parameters: conjxt conjx conjy + +0 # axpyf +-1 # dimensions: m +?? # parameters: conja conjx + +0 # dotxf +-1 # dimensions: m +?? # parameters: conjat conjx + +0 # dotxaxpyf +-1 # dimensions: m +???? # parameters: conjat conja conjw conjx + + +# --- Level-2 -------------------------------------------------------------- + +0 # gemv +-1 -2 # dimensions: m n +?? # parameters: transa conjx + +0 # ger +-1 -2 # dimensions: m n +?? # parameters: conjx conjy + +0 # hemv +-1 # dimensions: m +??? # parameters: uploa conja conjx + +0 # her +-1 # dimensions: m +?? # parameters: uploc conjx + +0 # her2 +-1 # dimensions: m +??? # parameters: uploc conjx conjy + +0 # symv +-1 # dimensions: m +??? # parameters: uploa conja conjx + +0 # syr +-1 # dimensions: m +?? # parameters: uploc conjx + +0 # syr2 +-1 # dimensions: m +??? # parameters: uploc conjx conjy + +0 # trmv +-1 # dimensions: m +??? # parameters: uploa transa diaga + +0 # trsv +-1 # dimensions: m +??? # parameters: uploa transa diaga + + +# --- Level-3 micro-kernels ------------------------------------------------ + +0 # gemm +-1 # dimensions: k + +0 # trsm +? # parameters: uploa + +0 # gemmtrsm +-1 # dimensions: k +? # parameters: uploa + + +# --- Level-3 -------------------------------------------------------------- + +1 # gemm +-1 -1 -1 # dimensions: m n k +?? # parameters: transa transb + +0 # gemmt +-1 -1 # dimensions: m k +??? # parameters: uploc transa transb + +0 # hemm +-1 -1 # dimensions: m n +?? # parameters: side uploa + +0 # herk +-1 -1 # dimensions: m k +?? # parameters: uploc trans + +0 # her2k +-1 -1 # dimensions: m k +?? # parameters: uploc trans + +0 # symm +-1 -1 # dimensions: m n +?? # parameters: side uploa + +0 # syrk +-1 -1 # dimensions: m k +?? # parameters: uploc trans + +0 # syr2k +-1 -1 # dimensions: m k +?? # parameters: uploc trans + +0 # trmm +-1 -1 # dimensions: m n +???? # parameters: side uploa transa diaga + +0 # trmm3 +-1 -1 # dimensions: m n +????? # parameters: side uploa transa diaga transb + +0 # trsm +-1 -1 # dimensions: m n +???? # parameters: side uploa transa diaga + +# --- lpgemm -------------------------------------------------------------- + +0 # gemm_u8s8s32os32 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + +0 # gemm_u8s8s32os8 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + +0 # gemm_f32f32f32of32 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + +0 # gemm_u8s8s16os16 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + +0 # gemm_u8s8s16os8 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + +0 # gemm_bf16bf16f32of32 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + +0 # gemm_bf16bf16f32obf16 +-1 -1 -1 # dimensions: m n k +nn # parameters: transa transb + diff --git a/gtestsuite/inputfile.txt b/gtestsuite/inputfile.txt new file mode 100644 index 000000000..c3f727903 --- /dev/null +++ b/gtestsuite/inputfile.txt @@ -0,0 +1,3 @@ +dgemm_ D N N 1000 3000 2000 0.900000 0.000000 4000 5000 -1.100000 0.000000 6000 +dgemv_ D N 1 14 -1.000000 0.000000 10000 1 1.000000 0.000000 1 +sgemm_ S N N 7 1026 1026 0.900000 0.000000 1026 1026 -1.100000 0.000000 1026 \ No newline at end of file diff --git a/gtestsuite/lib/libgtest.a b/gtestsuite/lib/libgtest.a new file mode 100644 index 0000000000000000000000000000000000000000..17e3f5c2ceb02e0d07d983f3e6a1cc20300683f2 GIT binary patch literal 2125482 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

S6bC}6cxeBGrmZLlQaoDbpiJ*n` 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 + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + unsigned int pci = 0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_randv( params, iface, params->dc_str[dci], + params->sc_str[sci], &m); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_randm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + unsigned int pci = 0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_randm( params, iface, params->dc_str[dci], + params->sc_str[sci], &mn); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} +/********************* End Of Utility Operations *****************************/ +/* */ +/*****************************Level-1V Operations******************************/ +void* libblis_test_addv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_addv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_amaxv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_amaxv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_axpbyv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_axpbyv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_axpyv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_axpyv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_copyv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_copyv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_dotv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_dotv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_dotxv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_dotxv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_normfv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_normfv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_scal2v_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_scal2v( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_scalv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_scalv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_setv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_setv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_subv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_subv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} +/********************* End Of Level-1V Operations *****************************/ +/* */ +/*****************************Level-1F Operations******************************/ +void* libblis_test_xpbyv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_xpbyv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_axpy2v_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_axpy2v( params, iface, + params->dc_str[dci], params->pc_str[pci], + params->sc_str[sci], &m, params->alpha[a], + params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_dotaxpyv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_dotaxpyv( params, iface, + params->dc_str[dci], params->pc_str[pci], + params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_axpyf_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_axpyf( params, op, iface, + params->dc_str[dci], params->pc_str[pci], + params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_dotxf_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_dotxf( params, op, iface, + params->dc_str[dci], params->pc_str[pci], + params->sc_str[sci], &m, params->alpha[a], + params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_dotxaxpyf_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_dotxaxpyf( params, op, iface, + params->dc_str[dci], params->pc_str[pci], + params->sc_str[sci], &m, params->alpha[a], + params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} +/********************* End Of Level-1F Operations *****************************/ +/* */ +/*****************************Level-1M Operations******************************/ +void* libblis_test_addm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_addm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_axpym_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_axpym( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a] ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_copym_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_copym( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_normfm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_normfm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_scal2m_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_scal2m( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_scalm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_scalm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_setm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_setm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_subm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_subm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn ); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_xpbym_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_xpbym( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} +/********************* End Of Level-1M Operations *****************************/ +/* */ +/*****************************Level-2 Operations*******************************/ +void* libblis_test_gemv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_ger_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_ger( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_hemv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_hemv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_her_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_her( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_her2_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_her2( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_symv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_symv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_syr_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_syr( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_syr2_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_syr2( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_trmv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_trmv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_trsv_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t m; + double resid = 0.0; + + if( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + m = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_trsv( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &m, params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &m, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} +/********************* End Of Level-2 Operations *****************************/ +/* */ +/*****************************Level-3 Operations******************************/ +void* libblis_test_gemm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mnk, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemmt_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t nk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + nk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemmt( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &nk, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &nk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_hemm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_hemm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_herk_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t nk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + nk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_herk( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &nk, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &nk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_her2k_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_her2k( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_symm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_symm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_syrk_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t nk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + nk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_syrk( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &nk, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &nk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_syr2k_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_syr2k( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_trmm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_trmm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_trmm3_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_trmm3( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_trsm_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + iface_t iface = tdata->iface; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mn; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mn = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested datatypes. + for ( unsigned int dci = 0; dci < params->n_dt_combos; ++dci ) { + unsigned int n = params->indn[dci]; + // Loop over induced methods (or just BLIS_NAT). + for( unsigned int ind = 0 ; ind < n ; ++ind) { + mt = ind_enable_get_str(params, dci, ind, op); + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_trsm( params, iface, params->dc_str[dci], + params->pc_str[pci], params->sc_str[sci], &mn, + params->alpha[a]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[dci], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, + mt, dci, pci, sci, buffer); + + displayProps(buffer, params, op, &mn, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} +/********************* End Of Level-3 Operations *****************************/ +/* */ +/*****************************LPGEMM Operations ******************************/ +void* libblis_test_gemm_u8s8s32os32_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_u8s8s32os32( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemm_u8s8s32os8_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_u8s8s32os8( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemm_f32f32f32of32_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_f32f32f32of32( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemm_u8s8s16os8_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_u8s8s16os8( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemm_u8s8s16os16_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_u8s8s16os16( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemm_bf16bf16f32obf16_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_bf16bf16f32obf16( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +void* libblis_test_gemm_bf16bf16f32of32_thread_entry( void* tdata_void ) { + thread_data_t* tdata = (thread_data_t*)tdata_void; + test_params_t* params = tdata->params; + test_op_t* op = tdata->op; + const char* op_str = tdata->str; + printres_t* pfr = tdata->pfr; + ind_t mt = BLIS_NAT; + char label_str[128]; + tensor_t mnk; + double resid = 0.0; + + if ( tdata->id == 0 ) { + libblis_test_build_col_labels_string( params, op, label_str ); + libblis_test_fprintf( stdout, "\n%s\n", label_str ); + } + + params->dc_str[0][0] = 's'; + tensor_t *dim = params->dim; + unsigned int ndim = params->ndim; + + // Loop over the requested problem sizes. + for(unsigned int i = 0 ; i < ndim ; i++) { + mnk = dim[i]; + // Loop over the requested storage schemes. + for ( unsigned int sci = 0; sci < params->n_store_combos; ++sci ) { + // Loop over the requested parameter combinations. + for ( unsigned int pci = 0; pci < params->n_param_combos; ++pci ) { + // Loop over the alpha values. + for ( unsigned int a = 0; a < params->nab; ++a ) { + // Loop over the beta values. + for ( unsigned int b = 0; b < params->nab; ++b ) { + if ( tdata->xc % tdata->nt != tdata->id ) { + tdata->xc++; + continue; + } + resid = libblis_test_op_gemm_bf16bf16f32of32( params, params->pc_str[pci], + params->sc_str[sci], &mnk, params->alpha[a], params->beta[b]); + + pfr->tcnt++; + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, + 0, pci, sci, buffer); + + displayProps(buffer, params, op, &mnk, resid, res_str, pfr); + + tdata->xc += 1; + } + } + } + } + } + + // Wait for all other threads so that the output stays organized. + bli_pthread_barrier_wait( tdata->barrier ); + + return 0; +} + +/*************************** End Of Operations *******************************/ +/* END OF OPERATIONS */ +/*****************************************************************************/ \ No newline at end of file diff --git a/gtestsuite/src/blis_api.h b/gtestsuite/src/blis_api.h new file mode 100644 index 000000000..f7b860f72 --- /dev/null +++ b/gtestsuite/src/blis_api.h @@ -0,0 +1,737 @@ +#ifndef BLIS_API_H +#define BLIS_API_H + +#include "blis_utils.h" +#include "blis_inpfile.h" + +char* libblis_test_get_result + ( + double resid, + const thresh_t* thresh, + char* dc_str, + test_params_t* params + ); + +void fill_string_with_n_spaces( char* str, unsigned int n_spaces ); + +void libblis_test_build_function_string + ( + char* prefix_str, + opid_t opid, + ind_t method, + char* ind_str, + const char* op_str, + unsigned int is_mixed_dt, + char* dc_str, + unsigned int n_param_combos, + char* pc_str, + char* sc_str, + char* funcname_str + ); + +void libblis_test_build_dims_string(test_op_t* op, tensor_t* dim, char* dims_str); + +static char* libblis_test_result( double resid, const thresh_t* thresh, + char* dc_str, test_params_t* params ) { + char* r_val; + return r_val = libblis_test_get_result ( resid, thresh, dc_str, params ); +} + +static void libblis_build_function_string( test_params_t* params, + opid_t opid, const char *op_str, ind_t method, unsigned int dci, + unsigned int pci, unsigned int sci, char* fucnptr ) { + + char* ind_str = NULL; + char* str = NULL; + if( params->api == API_CBLAS ) + str = (char*)CBLAS_FILEDATA_PREFIX_STR; + else if( params->api == API_BLAS ) + str = (char*)BLAS_FILEDATA_PREFIX_STR; + else + str = (char*)BLIS_FILEDATA_PREFIX_STR; + + if ( method != BLIS_NAT ) { + ind_str = bli_ind_get_impl_string( method ); + } + + // Build a string unique to the operation, datatype combo, + // parameter combo, and storage combo being tested. + libblis_test_build_function_string( str, + opid, method, ind_str, op_str, params->is_mixed_dt, + params->dc_str[dci], params->n_param_combos, params->pc_str[pci], + params->sc_str[sci], fucnptr ); +} + +static void displayProps( const char* fucnptr, test_params_t* prms, test_op_t* op, + tensor_t* dim, double& resid, char *ps, printres_t *ptr) +{ + char blank_str[32]; + char dims_str[64]; + string sas = ps ; + string sfs = BLIS_TEST_FAIL_STRING ; + string sos = BLIS_TEST_OVERFLOW_STRING; + string sus = BLIS_TEST_UNDERFLOW_STRING; + string sps = BLIS_TEST_PASS_STRING; + string sws = BLIS_TEST_WARN_STRING; + + // Compute the number of spaces we have left to fill given + // length of our operation's name. + unsigned int n_spaces = MAX_FUNC_STRING_LENGTH - strlen( fucnptr ); + fill_string_with_n_spaces( blank_str, n_spaces ); + + // Print all dimensions to a single string. + libblis_test_build_dims_string( op, dim, dims_str ); + +/* if(( prms->passflag && ( strcmp(ps, BLIS_TEST_FAIL_STRING) != 0 )) || + ( prms->oruflw && (( strcmp(ps, BLIS_TEST_OVERFLOW_STRING) != 0 ) || + ( strcmp(ps, BLIS_TEST_UNDERFLOW_STRING) != 0 ))))*/ + if( prms->passflag && (( sas == sps) || ( sas == sws)) ) + { + fprintf( stdout, + "%s%s %s %8.2le %s\n", + fucnptr, blank_str, + dims_str, resid, + ps ); + } + +/* if(( strcmp(ps, BLIS_TEST_FAIL_STRING) == 0 ) || + ( prms->oruflw && (( strcmp(ps, BLIS_TEST_OVERFLOW_STRING) == 0 ) || + ( strcmp(ps, BLIS_TEST_UNDERFLOW_STRING) == 0 ))))*/ + if(( sas == sfs) || ( prms->oruflw && (( sas == sos) || ( sas == sus)))) + { + fprintf( stdout, + "%s%s %s %8.2le %s\n", + fucnptr, blank_str, + dims_str, resid, + ps ); + + ptr->cntf++; + } +} + +double libblis_test_op_randv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_randm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_addv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_amaxv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_axpbyv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_axpyv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_copyv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_dotv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_dotxv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_normfv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_scal2v + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_scalv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_setv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_xpbyv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_subv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_axpy2v + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_dotaxpyv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_axpyf + ( + test_params_t* params, + test_op_t* op, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_dotxf + ( + test_params_t* params, + test_op_t* op, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_dotxaxpyf + ( + test_params_t* params, + test_op_t* op, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_addm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_axpym + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_copym + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_normfm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_scal2m + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_scalm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_setm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_subm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim + ); + +double libblis_test_op_xpbym + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_gemv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_ger + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_hemv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_her + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_her2 + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_symv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_syr + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_syr2 + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_trmv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_trsv + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_gemm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemmt + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_hemm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_herk + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_her2k + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_symm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_syrk + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_syr2k + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_trmm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_trmm3 + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_trsm + ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha + ); + +double libblis_test_op_gemm_u8s8s32os32 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemm_u8s8s32os8 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemm_f32f32f32of32 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemm_u8s8s16os8 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemm_u8s8s16os16 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemm_bf16bf16f32obf16 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +double libblis_test_op_gemm_bf16bf16f32of32 + ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha, + atom_t beta + ); + +#endif // BLIS_API_H + diff --git a/gtestsuite/src/blis_inpfile.cpp b/gtestsuite/src/blis_inpfile.cpp new file mode 100644 index 000000000..511370f16 --- /dev/null +++ b/gtestsuite/src/blis_inpfile.cpp @@ -0,0 +1,3651 @@ +#include "blis_utils.h" +#include "blis_api.h" +#include "blis_inpfile.h" + +int libblis_test_read_randv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld \n", + api_name, &dt, &m) == 3) { + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "randv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_randv( params, iface, params->dc_str[0], + params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_randm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld %ld\n", + api_name, &dt, &m, &n) == 4) { + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "randm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_randm( params, iface, params->dc_str[0], params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_addv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld\n", + api_name, &dt, &transx, &m ) == 4) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "addv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_addv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_amaxv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + + // Variables extracted from the logs which are used by bench + char stor_scheme; + inc_t incx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld %ld \n", api_name, &dt, &m, &incx) == 4) + { + params->sc_str[0][0] = stor_scheme; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "amaxv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_amaxv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_axpbyv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t incx, incy; + double alpha_r, alpha_i, beta_r, beta_i; + + if(sscanf(str, "%s %c %ld %lf %lf %ld %lf %lf %ld\n", + api_name, &dt, &m, &alpha_r, &alpha_i, &incx, + &beta_r, &beta_i, &incy ) == 9 ){ + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + } + + double resid = 0.0; + const char* op_str = "axpbyv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_axpbyv( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_axpyv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conjx = 'n'; + stor_scheme = 'c'; + inc_t incx, incy; + double alpha_r, alpha_i; + + if(sscanf(str, "%s %c %ld %lf %lf %ld %ld \n", + api_name, &dt, &m, &alpha_r, &alpha_i, &incx, &incy ) == 7) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(conjx) ){ + params->pc_str[0][0] = tolower(conjx); + } else { + params->pc_str[0][0] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + + params->dim[0].m = m; + params->dim[0].n = 0; + params->dim[0].k = 0; + } + + double resid = 0.0; + const char* op_str = "axpyv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_axpyv( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_copyv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t incx, incy; + + if(sscanf(str, "%s %c %ld %ld %ld\n", + api_name, &dt, &m, &incx, &incy) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = 0; + params->dim[0].k = 0; + } + + double resid = 0.0; + const char* op_str = "copyv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_copyv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_dotv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + inc_t incx, incy; + + if(sscanf(str, "%s %c %ld %ld %ld\n", + api_name, &dt, &m, &incx, &incy) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + params->pc_str[0][0] = 'n'; + params->pc_str[0][1] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "dotv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_dotv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_dotxv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conjx, conjy; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %ld\n", + api_name, &dt, &conjx, &conjy, &m) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(conjx) ){ + params->pc_str[0][0] = tolower(conjx); + } else { + params->pc_str[0][0] = conjx; + } + + if (isalpha(conjy) ){ + params->pc_str[0][1] = tolower(conjy); + } else { + params->pc_str[0][1] = conjy; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "dotxv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_dotxv( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_normfv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld\n", + api_name, &dt, &m) == 3) { + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "normfv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_normfv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, + params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_scal2v_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld \n", + api_name, &dt, &transx, &m ) == 4) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "scal2v"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_scal2v( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params,op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_scalv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + double alpha_r, alpha_i; + inc_t incx; + + if(sscanf(str, "%s %c %lf %lf %ld %ld\n", + api_name, &dt, &alpha_r, &alpha_i, &m, &incx) == 6){ + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + } + + double resid = 0.0; + const char* op_str = "scalv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_scalv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_setv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld \n", + api_name, &dt, &m) == 3) { + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "setv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_setv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_subv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld\n", + api_name, &dt, &transx, &m ) == 4) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "subv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_subv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_xpbyv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + char transx; + + if(sscanf(str, "%s %c %c %ld\n", + api_name, &dt, &transx, &m) == 4){ + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "xpbyv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_xpbyv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_axpyf_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conja, conjx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %ld\n", + api_name, &dt, &conja, &conjx, &m ) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(conja) ){ + params->pc_str[0][0] = tolower(conja); + } else { + params->pc_str[0][0] = conja; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "axpyf"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_axpyf( params, op, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_axpy2v_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conjx, conjy; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %ld\n", + api_name, &dt, &conjx, &conjy, &m ) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(conjx) ){ + params->pc_str[0][0] = tolower(conjx); + } else { + params->pc_str[0][0] = conjx; + } + + if (isalpha(conjy) ){ + params->pc_str[0][1] = tolower(conjy); + } else { + params->pc_str[0][1] = conjy; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "axpy2v"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_axpy2v( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_dotxf_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conjat, conjx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %ld\n", + api_name, &dt, &conjat, &conjx, &m) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(conjat) ){ + params->pc_str[0][0] = tolower(conjat); + } else { + params->pc_str[0][0] = conjat; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "dotxf"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_dotxf( params, op, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_dotaxpyv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conjxt, conjx, conjy; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %c %ld\n", + api_name, &dt, &conjxt, &conjx, &conjy, &m) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(conjxt) ){ + params->pc_str[0][0] = tolower(conjxt); + } else { + params->pc_str[0][0] = conjxt; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(conjy) ){ + params->pc_str[0][1] = tolower(conjy); + } else { + params->pc_str[0][1] = conjy; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "dotaxpyv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_dotaxpyv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_dotxaxpyf_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, conjat, conja, conjw, conjx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %c %c %ld\n", + api_name, &dt, &conjat, &conja, &conjw, &conjx, &m) == 7) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + params->sc_str[0][3] = stor_scheme; + + if (isalpha(conjat) ){ + params->pc_str[0][0] = tolower(conjat); + } else { + params->pc_str[0][0] = conjat; + } + + if (isalpha(conja) ){ + params->pc_str[0][1] = tolower(conja); + } else { + params->pc_str[0][1] = conja; + } + + if (isalpha(conjw) ){ + params->pc_str[0][2] = tolower(conjw); + } else { + params->pc_str[0][2] = conjw; + } + + if (isalpha(conjx) ){ + params->pc_str[0][3] = tolower(conjx); + } else { + params->pc_str[0][3] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "dotxaxpyf"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_dotxaxpyf( params, op, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_addm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n ) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "addm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_addm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t ); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_axpym_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n ) == 5) { + + params->sc_str[0][0] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "axpym"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_axpym( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_copym_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n ) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "copym"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_copym( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_normfm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m,n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld %ld\n", + api_name, &dt, &m, &n) == 4) { + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "normfm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_normfm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_scal2m_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n ) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "scal2m"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_scal2m( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_scalm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n ) == 5) { + + params->sc_str[0][0] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "scalm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_scalm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_setm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %ld %ld\n", + api_name, &dt, &m, &n) == 4) { + + params->sc_str[0][0] = stor_scheme; + + params->pc_str[0][0] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "setm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_setm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_subm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme, transx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n ) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "subm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_subm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_xpbym_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m,n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + char transx; + + if(sscanf(str, "%s %c %c %ld %ld\n", + api_name, &dt, &transx, &m, &n) == 5){ + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(transx) ){ + params->pc_str[0][0] = tolower(transx); + } else { + params->pc_str[0][0] = transx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + } + + double resid = 0.0; + const char* op_str = "xpbym"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_xpbym( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + + // Variables extracted from the logs which are used by bench + char stor_scheme, transA; + double alpha_r, beta_r, alpha_i, beta_i; + inc_t lda; + inc_t incx, incy; + + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %ld %ld %lf %lf %ld %ld %lf %lf %ld\n", + api_name, &dt, &transA, &m, &n, &alpha_r, &alpha_i, &lda, + &incx, &beta_r, &beta_i, &incy) == 12) + { + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(transA) ){ + params->pc_str[0][0] = tolower(transA); + } else { + params->pc_str[0][0] = transA; + } + + if(transA == 'C' || transA == 'c') + params->pc_str[0][1] = 'c'; + else /*if(transA == 'N' || transA == 'n')*/ + params->pc_str[0][1] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + } + + double resid = 0.0; + const char* op_str = "gemv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemv( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_ger_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + double alpha_r, alpha_i; + inc_t lda; + inc_t incx, incy; + + if(sscanf(str, "%s %c %ld %ld %lf %lf %ld %ld %ld\n", + api_name, &dt, &m, &n, &alpha_r, &alpha_i, &incx, &incy, &lda) == 9) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][0] = 'n'; + params->pc_str[0][1] = 'n'; + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + + params->ld[0] = lda; + } + + double resid = 0.0; + const char* op_str = "ger"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_ger( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_hemv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, uploa, conja, conjx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %c %ld\n", + api_name, &dt, &uploa, &conja, &conjx, &m) == 6) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uploa) ){ + params->pc_str[0][0] = tolower(uploa); + } else { + params->pc_str[0][0] = uploa; + } + + if (isalpha(conja) ){ + params->pc_str[0][1] = tolower(conja); + } else { + params->pc_str[0][1] = conja; + } + + if (isalpha(conjx) ){ + params->pc_str[0][2] = tolower(conjx); + } else { + params->pc_str[0][2] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "hemv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_hemv( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_her_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, uploa, conjx; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %ld\n", + api_name, &dt, &uploa, &conjx, &m) == 5) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(uploa) ){ + params->pc_str[0][0] = tolower(uploa); + } else { + params->pc_str[0][0] = uploa; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "her"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_her( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_her2_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme, uploa, conjx, conjy; + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %c %ld\n", + api_name, &dt, &uploa, &conjx, &conjy, &m) == 6) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uploa) ){ + params->pc_str[0][0] = tolower(uploa); + } else { + params->pc_str[0][0] = uploa; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(conjy) ){ + params->pc_str[0][2] = tolower(conjy); + } else { + params->pc_str[0][2] = conjy; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + } + + double resid = 0.0; + const char* op_str = "her2"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_her2( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_symv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t lda; + char uplo, conja, conjx; + double alpha_r, beta_r, alpha_i, beta_i; + + if(sscanf(str, "%s %c %c %c %c %ld %lf %lf %lu %lf %lf \n", + api_name, &dt, &uplo, &conja, &conjx, &m, &alpha_r, &alpha_i, + &lda, &beta_r, &beta_i) == 11) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(conja) ){ + params->pc_str[0][1] = tolower(conja); + } else { + params->pc_str[0][1] = conja; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][2] = tolower(dt); + } else { + params->dc_str[0][2] = dt; + } + + params->dim[0].m = m; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + } + + double resid = 0.0; + const char* op_str = "symv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_symv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_syr_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + char uplo, conjx; + double alpha_r, alpha_i; + + if(sscanf(str, "%s %c %c %c %ld %lf %lf \n", + api_name, &dt, &uplo, &conjx, &m, &alpha_r, &alpha_i) == 7) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + } + + double resid = 0.0; + const char* op_str = "syr"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_syr( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_syr2_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + char uplo, conjx; + double alpha_r, alpha_i; + + if(sscanf(str, "%s %c %c %c %ld %lf %lf \n", + api_name, &dt, &uplo, &conjx, &m, &alpha_r, &alpha_i) == 7) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(conjx) ){ + params->pc_str[0][1] = tolower(conjx); + } else { + params->pc_str[0][1] = conjx; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + } + + double resid = 0.0; + const char* op_str = "syr2"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_syr2( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params,op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_trmv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda; + char uploa, transA, diaga; + inc_t incx; + + if(sscanf(str, "%s %c %c %c %c %ld %ld %ld\n", + api_name, &dt, &uploa, &transA, &diaga, &m, &lda, &incx) == 8){ + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(uploa) ){ + params->pc_str[0][0] = tolower(uploa); + } else { + params->pc_str[0][0] = uploa; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(diaga) ){ + params->pc_str[0][2] = tolower(diaga); + } else { + params->pc_str[0][2] = diaga; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + + params->ld[0] = lda; + } + + double resid = 0.0; + const char* op_str = "trmv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_trmv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_trsv_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda; + char uploa, transA, diaga; + inc_t incx; + + if(sscanf(str, "%s %c %c %c %c %ld %ld %ld\n", + api_name, &dt, &uploa, &transA, &diaga, &m, &lda, &incx) == 8){ + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(uploa) ){ + params->pc_str[0][0] = tolower(uploa); + } else { + params->pc_str[0][0] = uploa; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(diaga) ){ + params->pc_str[0][2] = tolower(diaga); + } else { + params->pc_str[0][2] = diaga; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + + params->ld[0] = lda; + } + + double resid = 0.0; + const char* op_str = "trsv"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_trsv( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n, k; + char dt; + + // Variables extracted from the logs which are used by bench + char stor_scheme, transA, transB; + double alpha_r, beta_r, alpha_i, beta_i; + inc_t lda, ldb, ldc; + + stor_scheme = 'c'; + + if(sscanf(str, "%s %c %c %c %ld %ld %ld %lf %lf %ld %ld %lf %lf %ld[^\n]", + api_name, &dt, &transA, &transB, &m, &n, &k, &alpha_r, &alpha_i, + &lda, &ldb, &beta_r, &beta_i, &ldc) == 14) { + + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(transA) ){ + params->pc_str[0][0] = tolower(transA); + } else { + params->pc_str[0][0] = transA; + } + + if (isalpha(transB) ){ + params->pc_str[0][1] = tolower(transB); + } else { + params->pc_str[0][1] = transB; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "gemm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemmt_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t lda, ldb, ldc; + char transA, transB, uplo; + double alpha_r, beta_r, alpha_i, beta_i; + + if(sscanf(str,"%s %c %c %ld %ld %lu %lu %lu %c %c %lf %lf %lf %lf\n",\ + api_name, &dt, &uplo, &m, &n, &lda, &ldb, &ldc, &transA, &transB, + &alpha_r, &alpha_i, &beta_r, &beta_i) == 14) { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if(isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(transB) ){ + params->pc_str[0][2] = tolower(transB); + } else { + params->pc_str[0][2] = transB; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "gemmt"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemmt( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_hemm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda, ldb, ldc; + char side, uploa ; + double alpha_r, alpha_i, beta_r, beta_i; + + if(sscanf(str, "%s %c %c %c %ld %ld %lf %lf %ld %ld %lf %lf %ld\n", + api_name, &dt, &side, &uploa, &m, &n, &alpha_r, &alpha_i, + &lda, &ldb, &beta_r, &beta_i, &ldc) == 13) { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(side) ){ + params->pc_str[0][0] = tolower(side); + } else { + params->pc_str[0][0] = side; + } + + if (isalpha(uploa) ){ + params->pc_str[0][1] = tolower(uploa); + } else { + params->pc_str[0][1] = uploa; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "hemm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_hemm( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_herk_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t lda, ldc; + char transA, uplo; + double alpha_r, beta_r, alpha_i, beta_i; + + if(sscanf(str, "%s %c %c %c %ld %ld %lf %lf %lu %lf %lf %lu\n", + api_name, &dt, &uplo, &transA, &m, &n, &alpha_r, &alpha_i, + &lda, &beta_r, &beta_i, &ldc) == 12) { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "herk"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_herk( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_her2k_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t lda, ldc; + char transA, transB, uplo; + double alpha_r, beta_r, alpha_i, beta_i; + + if(sscanf(str, "%s %c %c %c %c %ld %ld %lf %lf %lu %lf %lf %lu\n", + api_name, &dt, &uplo, &transA, &transB, &m, &n, + &alpha_r, &alpha_i, &lda, &beta_r, &beta_i, &ldc) == 12) + { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(transB) ){ + params->pc_str[0][1] = tolower(transB); + } else { + params->pc_str[0][1] = transB; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "her2k"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_her2k( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_symm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda, ldb, ldc; + char side, uploa ; + double alpha_r, alpha_i, beta_r, beta_i; + + if(sscanf(str, "%s %c %c %c %ld %ld %lf %lf %ld %ld %lf %lf %ld\n", + api_name, &dt, &side, &uploa, &m, &n, &alpha_r, &alpha_i, + &lda, &ldb, &beta_r, &beta_i, &ldc) == 13) { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(side) ){ + params->pc_str[0][0] = tolower(side); + } else { + params->pc_str[0][0] = side; + } + + if (isalpha(uploa) ){ + params->pc_str[0][1] = tolower(uploa); + } else { + params->pc_str[0][1] = uploa; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "symm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_symm( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_syrk_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t lda, ldc; + char transA, uplo; + double alpha_r, beta_r, alpha_i, beta_i; + + if(sscanf(str, "%s %c %c %c %ld %ld %lf %lf %lu %lf %lf %lu\n", + api_name, &dt, &uplo, &transA, &m, &n, &alpha_r, &alpha_i, + &lda, &beta_r, &beta_i, &ldc) == 12) { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "syrk"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_syrk( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_syr2k_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + inc_t lda, ldc; + char transA, transB, uplo; + double alpha_r, beta_r, alpha_i, beta_i; + + if(sscanf(str, "%s %c %c %c %c %ld %ld %lf %lf %lu %lf %lf %lu\n", + api_name, &dt, &uplo, &transA, &transB, &m, &n, + &alpha_r, &alpha_i, &lda, &beta_r, &beta_i, &ldc) == 12) + { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + if (isalpha(uplo) ){ + params->pc_str[0][0] = tolower(uplo); + } else { + params->pc_str[0][0] = uplo; + } + + if (isalpha(transA) ){ + params->pc_str[0][1] = tolower(transA); + } else { + params->pc_str[0][1] = transA; + } + + if (isalpha(transB) ){ + params->pc_str[0][1] = tolower(transB); + } else { + params->pc_str[0][1] = transB; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "syr2k"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_syr2k( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_trmm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda, ldb; + char side, uploa, transa, diaga; + double alpha_r, alpha_i; + + if(sscanf(str, "%s %c %c %c %c %c %ld %ld %ld %ld %lf %lf\n", + api_name, &dt, &side, &uploa, &transa, &diaga, &m, &n, + &lda, &ldb, &alpha_r, &alpha_i) == 12) { + + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(side) ){ + params->pc_str[0][0] = tolower(side); + } else { + params->pc_str[0][0] = side; + } + + if (isalpha(uploa) ){ + params->pc_str[0][1] = tolower(uploa); + } else { + params->pc_str[0][1] = uploa; + } + + if (isalpha(transa) ){ + params->pc_str[0][2] = tolower(transa); + } else { + params->pc_str[0][2] = transa; + } + + if (isalpha(diaga) ){ + params->pc_str[0][3] = tolower(diaga); + } else { + params->pc_str[0][3] = diaga; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + } + + double resid = 0.0; + const char* op_str = "trmm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_trmm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_trmm3_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda, ldb, ldc; + char side, uploa, transa, transb, diaga; + double alpha_r, alpha_i, beta_r, beta_i; + + if(sscanf(str, "%s %c %c %c %c %c %c %ld %ld %lf %lf %ld %ld %lf %lf %ld\n", + api_name, &dt, &side, &uploa, &transa, &transb, &diaga, &m, &n, + &alpha_r, &alpha_i, &lda, &ldb, &beta_r, &beta_i, &ldc) == 16) + { + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(side) ){ + params->pc_str[0][0] = tolower(side); + } else { + params->pc_str[0][0] = side; + } + + if (isalpha(uploa) ){ + params->pc_str[0][1] = tolower(uploa); + } else { + params->pc_str[0][1] = uploa; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + params->beta->real = beta_r; + params->beta->imag = beta_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + } + + double resid = 0.0; + const char* op_str = "trmm3"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_trmm3( params, iface, params->dc_str[0], params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_trsm_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[10]; + dim_t m, n; + char dt; + char stor_scheme; + stor_scheme = 'c'; + + dim_t lda, ldb; + char side, uploa, transa, diaga; + double alpha_r, alpha_i; + + if(sscanf(str, "%s %c %c %c %c %c %ld %ld %ld %ld %lf %lf\n", + api_name, &dt, &side, &uploa, &transa, &diaga, &m, &n, + &lda, &ldb, &alpha_r, &alpha_i) == 12) { + + if( m == lda ) + stor_scheme = 'c'; + if( n == lda ) + stor_scheme = 'r'; + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + + if (isalpha(side) ){ + params->pc_str[0][0] = tolower(side); + } else { + params->pc_str[0][0] = side; + } + + if (isalpha(uploa) ){ + params->pc_str[0][1] = tolower(uploa); + } else { + params->pc_str[0][1] = uploa; + } + + if (isalpha(transa) ){ + params->pc_str[0][2] = tolower(transa); + } else { + params->pc_str[0][2] = transa; + } + + if (isalpha(diaga) ){ + params->pc_str[0][3] = tolower(diaga); + } else { + params->pc_str[0][3] = diaga; + } + + if (isalpha(dt) ){ + params->dc_str[0][0] = tolower(dt); + } else { + params->dc_str[0][0] = dt; + } + + params->dim[0].m = m; + params->dim[0].n = n; + + params->alpha->real = alpha_r; + params->alpha->imag = alpha_i; + + params->ld[0] = lda; + params->ld[1] = ldb; + } + + double resid = 0.0; + const char* op_str = "trsm"; + ind_t mt = BLIS_NAT; + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_trsm( params, iface, params->dc_str[0], + params->pc_str[0], params->sc_str[0], &t, params->alpha[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_u8s8s32os32_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_u8s8s32os32"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_u8s8s32os32( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_u8s8s32os8_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_u8s8s32os8"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_u8s8s32os8( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_f32f32f32of32_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_f32f32f32of32"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_f32f32f32of32( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_u8s8s16os16_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_u8s8s16os16"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_u8s8s16os16( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_u8s8s16os8_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_u8s8s16os8"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_u8s8s16os8( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_bf16bf16f32of32_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_bf16bf16f32of32"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_bf16bf16f32of32( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +int libblis_test_read_gemm_bf16bf16f32obf16_params( char* str, test_op_t* op, + test_params_t* params, printres_t* pfr) { + char api_name[25]; + dim_t m, n, k; + char stor_scheme = 'c'; + + // Variables extracted from the logs which are used by bench + inc_t lda, ldb, ldc; + char op_t; + + if( sscanf( str, "%s %c %c %ld %ld %ld %ld %ld %ld\n", + api_name, &stor_scheme, &op_t, &m, &n, &k, + &lda, &ldb, &ldc ) == 9 ) { + + params->sc_str[0][0] = stor_scheme; + params->sc_str[0][1] = stor_scheme; + params->sc_str[0][2] = stor_scheme; + + params->pc_str[0][1] = 'n'; + params->pc_str[0][2] = 'n'; + + params->dim[0].m = m; + params->dim[0].n = n; + params->dim[0].k = k; + + params->ld[0] = lda; + params->ld[1] = ldb; + params->ld[2] = ldc; + + params->op_t = op_t; + + params->alpha->real = 2; + params->alpha->imag = 0; + params->beta->real = 9; + params->beta->imag = 0; + + } + + double resid = 0.0; + const char* op_str = "gemm_bf16bf16f32obf16"; + params->dc_str[0][0] = 's'; + ind_t mt = BLIS_NAT; + tensor_t *dim = params->dim; + tensor_t t = dim[0]; + + resid = libblis_test_op_gemm_bf16bf16f32obf16( params, params->pc_str[0], + params->sc_str[0], &t, params->alpha[0], params->beta[0]); + + char* res_str = libblis_test_result (resid, thresh, params->dc_str[0], params ); + + char buffer[125]; + libblis_build_function_string(params, op->opid, op_str, mt, 0, 0, 0, buffer); + + displayProps(buffer, params, op, &t, resid, res_str, pfr); + + return 0; +} + +void libblis_read_api(test_ops_t* ops, opid_t opid, dimset_t dimset, + unsigned int n_params, test_op_t* op ) { + + if ( op->op_switch == ENABLE_ONLY ){ + return; + } + + // Initialize the operation type field. + op->opid = opid; + + op->op_switch = ENABLE_ONLY ; + + // Check the op_switch for the individual override value. + if ( op->op_switch == ENABLE_ONLY ) { + ops->indiv_over = TRUE; + } + + op->n_dims = libblis_test_get_n_dims_from_dimset( dimset ); + op->dimset = dimset; + + if ( op->n_dims > MAX_NUM_DIMENSIONS ) { + libblis_test_printf_error( "Detected too many dimensions (%u) in input file to store.\n", + op->n_dims ); + } + + if ( n_params > 0 ) { + op->n_params = n_params; + } + else { + op->n_params = 0; + strcpy( op->params, "" ); + } + + // Initialize the "test done" switch. + op->test_done = FALSE; + + // Initialize the parent pointer. + op->ops = ops; + + return; +} + +void libblis_read_inpops(string ss, test_params_t* params, test_ops_t* ops, + string api, printres_t* pfr){ + char str[125]; + strcpy( str, ss.c_str() ); + + /* Utility operations */ + if (api == "randv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->randv) ); + libblis_test_read_randv_params(str, &(ops->randv), params, pfr); + } else if(api == "randm") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 0, &(ops->randm) ); + libblis_test_read_randm_params(str, &(ops->randm), params, pfr); + } else + /* Level-1v */ + if( (api == "addv") || (api == "add") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->addv) ); + libblis_test_read_addv_params(str, &(ops->addv), params, pfr); + } else if( (api == "amaxv") || (api == "amax") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->amaxv) ); + libblis_test_read_amaxv_params(str, &(ops->amaxv), params, pfr); + } else if( (api == "axpbyv") || (api == "axpby") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->axpbyv) ); + libblis_test_read_axpbyv_params(str, &(ops->axpbyv), params, pfr); + } else if( (api == "axpyv") || (api == "axpy") ){ + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->axpyv) ); + libblis_test_read_axpyv_params(str, &(ops->axpyv), params, pfr); + } else if((api == "copyv") || (api == "copy") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->copyv) ); + libblis_test_read_copyv_params(str, &(ops->copyv), params, pfr); + } else if( (api == "dotv") || (api == "dot")) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->dotv) ); + libblis_test_read_dotv_params(str, &(ops->dotv), params, pfr); + } else if( (api == "dotxv") || (api == "dotx") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->dotxv) ); + libblis_test_read_dotxv_params(str, &(ops->dotxv), params, pfr); + } else if( (api == "normfv") || (api == "nrm2") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->normfv) ); + libblis_test_read_normfv_params(str, &(ops->normfv), params, pfr); + } else if( (api == "scalv") || (api == "scal") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->scalv) ); + libblis_test_read_scalv_params(str, &(ops->scalv), params, pfr); + } else if( (api == "scal2v") || (api == "scal2") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->scal2v) ); + libblis_test_read_scal2v_params(str, &(ops->scal2v), params, pfr); + } else if( (api == "setv") || (api == "set") ){ + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->setv) ); + libblis_test_read_setv_params(str, &(ops->setv), params, pfr); + } else if( (api == "subv") || (api == "sub") ) { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->subv) ); + libblis_test_read_subv_params(str, &(ops->subv), params, pfr); + } else if( (api == "xpbyv") || (api == "xpby") ){ + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->xpbyv) ); + libblis_test_read_xpbyv_params(str, &(ops->xpbyv), params, pfr); + } else + /* Level-1m */ + if(api == "addm") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->addm) ); + libblis_test_read_addm_params(str, &(ops->addm), params, pfr); + } else if(api == "axpym") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->axpym) ); + libblis_test_read_axpym_params(str, &(ops->axpym), params, pfr); + } else if(api == "copym") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->copym) ); + libblis_test_read_copym_params(str, &(ops->copym), params, pfr); + } else if(api == "normfm") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 0, &(ops->normfm) ); + libblis_test_read_normfm_params(str, &(ops->normfm), params, pfr); + } else if(api == "scalm") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->scalm) ); + libblis_test_read_scal2m_params(str, &(ops->scalm), params, pfr); + } else if(api == "scal2m") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->scal2m) ); + libblis_test_read_scalm_params(str, &(ops->scal2m), params, pfr); + } else if(api == "setm") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 0, &(ops->setm) ); + libblis_test_read_setm_params(str, &(ops->setm), params, pfr); + } else if(api == "subm") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->subm) ); + libblis_test_read_subm_params(str, &(ops->subm), params, pfr); + } else if(api == "xpbym") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->xpbym) ); + libblis_test_read_xpbym_params(str, &(ops->xpbym), params, pfr); + } else + /* Level-1f */ + if(api == "axpy2v") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->axpy2v) ); + libblis_test_read_axpy2v_params(str, &(ops->axpy2v), params, pfr); + } else if(api == "dotaxpyv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->dotaxpyv) ); + libblis_test_read_dotaxpyv_params(str, &(ops->dotaxpyv), params, pfr); + } else if(api == "axpyf") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MF, 2, &(ops->axpyf) ); + libblis_test_read_axpyf_params(str, &(ops->axpyf), params, pfr); + } else if(api == "dotxf") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MF, 2, &(ops->dotxf) ); + libblis_test_read_dotxf_params(str, &(ops->dotxf), params, pfr); + } else if(api == "dotxaxpyf") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MF, 4, &(ops->dotxaxpyf) ); + libblis_test_read_dotxaxpyf_params(str, &(ops->dotxaxpyf), params, pfr); + } else + /* Level-2 */ + if(api == "gemv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 2, &(ops->gemv) ); + libblis_test_read_gemv_params(str, &(ops->gemv), params, pfr); + } else if(api == "ger") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MN, 2, &(ops->ger) ); + libblis_test_read_ger_params(str, &(ops->ger), params, pfr); + } else if(api == "hemv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->hemv) ); + libblis_test_read_hemv_params(str, &(ops->hemv), params, pfr); + } else if(api == "her") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->her) ); + libblis_test_read_her_params(str, &(ops->her), params, pfr); + } else if(api == "her2") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->her2) ); + libblis_test_read_her2_params(str, &(ops->her2), params, pfr); + } else if(api == "symv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->symv) ); + libblis_test_read_symv_params(str, &(ops->symv), params, pfr); + } else if(api == "syr") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->syr) ); + libblis_test_read_syr_params(str, &(ops->syr), params, pfr); + } else if(api == "syr2") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->syr2) ); + libblis_test_read_syr2_params(str, &(ops->syr2), params, pfr); + } else if(api == "trmv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->trmv) ); + libblis_test_read_trmv_params(str, &(ops->trmv), params, pfr); + } else if(api == "trsv") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->trsv) ); + libblis_test_read_trsv_params(str, &(ops->trsv), params, pfr); + } else + /* Level-3 */ + if(api == "gemm") { + libblis_read_api( ops, BLIS_GEMM, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm) ); + libblis_test_read_gemm_params(str, &(ops->gemm), params, pfr); + } else if(api == "gemmt") { + libblis_read_api( ops, BLIS_GEMMT, BLIS_TEST_DIMS_MK, 3, &(ops->gemmt) ); + libblis_test_read_gemmt_params(str, &(ops->gemmt), params, pfr); + } else if(api == "hemm") { + libblis_read_api( ops, BLIS_HEMM, BLIS_TEST_DIMS_MN, 2, &(ops->hemm) ); + libblis_test_read_hemm_params(str, &(ops->hemm), params, pfr); + } else if(api == "herk") { + libblis_read_api( ops, BLIS_HERK, BLIS_TEST_DIMS_MK, 2, &(ops->herk) ); + libblis_test_read_herk_params(str, &(ops->herk), params, pfr); + } else if(api == "her2k") { + libblis_read_api( ops, BLIS_HER2K, BLIS_TEST_DIMS_MK, 2, &(ops->her2k) ); + libblis_test_read_her2k_params(str, &(ops->her2k), params, pfr); + } else if(api == "symm") { + libblis_read_api( ops, BLIS_SYMM, BLIS_TEST_DIMS_MN, 2, &(ops->symm) ); + libblis_test_read_symm_params(str, &(ops->symm), params, pfr); + } else if(api == "syrk") { + libblis_read_api( ops, BLIS_SYRK, BLIS_TEST_DIMS_MK, 2, &(ops->syrk) ); + libblis_test_read_syrk_params(str, &(ops->syrk), params, pfr); + } else if(api == "syr2k") { + libblis_read_api( ops, BLIS_SYR2K, BLIS_TEST_DIMS_MK, 2, &(ops->syr2k) ); + libblis_test_read_syr2k_params(str, &(ops->syr2k), params, pfr); + } else if(api == "trmm") { + libblis_read_api( ops, BLIS_TRMM, BLIS_TEST_DIMS_MN, 4, &(ops->trmm) ); + libblis_test_read_trmm_params(str, &(ops->trmm), params, pfr); + } else if(api == "trmm3") { + libblis_read_api( ops, BLIS_TRMM3, BLIS_TEST_DIMS_MN, 5, &(ops->trmm3) ); + libblis_test_read_trmm3_params(str, &(ops->trmm3), params, pfr); + } else if(api == "trsm") { + libblis_read_api( ops, BLIS_TRSM, BLIS_TEST_DIMS_MN, 4, &(ops->trsm) ); + libblis_test_read_trsm_params(str, &(ops->trsm), params, pfr); + } else + /* LPGEMM */ + if(api == "gemm_u8s8s32os32") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s32os32) ); + libblis_test_read_gemm_u8s8s32os32_params(str, &(ops->gemm_u8s8s32os32), params, pfr); + } + else if(api == "gemm_u8s8s32os8") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s32os8) ); + libblis_test_read_gemm_u8s8s32os8_params(str, &(ops->gemm_u8s8s32os8), params, pfr); + } + else if(api == "gemm_f32f32f32of32") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_f32f32f32of32) ); + libblis_test_read_gemm_f32f32f32of32_params(str, &(ops->gemm_f32f32f32of32), params, pfr); + } + else if(api == "gemm_u8s8s16os16") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s16os16) ); + libblis_test_read_gemm_u8s8s16os16_params(str, &(ops->gemm_u8s8s16os16), params, pfr); + } + else if(api == "gemm_u8s8s16os8") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s16os8) ); + libblis_test_read_gemm_u8s8s16os8_params(str, &(ops->gemm_u8s8s16os8), params, pfr); + } + else if(api == "gemm_bf16bf16f32of32") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_bf16bf16f32of32) ); + libblis_test_read_gemm_bf16bf16f32of32_params(str, &(ops->gemm_bf16bf16f32of32), params, pfr); + } + else if(api == "gemm_bf16bf16f32obf16") { + libblis_read_api( ops, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_bf16bf16f32obf16) ); + libblis_test_read_gemm_bf16bf16f32obf16_params(str, &(ops->gemm_bf16bf16f32obf16), params, pfr); + } + else { + printf("Invalid api option : "); + cout << ss << endl; + } + return; +} + +void libblis_read_inpprms(string str, test_params_t* params, test_ops_t* ops, printres_t* pfr) { + + stringstream ss; + string api, wrd; + + ss << str; + + str = ""; + ss >> wrd; + api = wrd.substr(1, (wrd.length()-2)); +// api = wrd.substr(0, wrd.length()); + + str = str + api; + + // Running loop till end of stream + while (!ss.eof()) { + + str = str + ' '; + + // Extracting word by word from stream + ss >> wrd; + + // Concatenating in the string to be returned + str = str + wrd; + } + + libblis_read_inpops(str, params, ops, api, pfr); +} diff --git a/gtestsuite/src/blis_inpfile.h b/gtestsuite/src/blis_inpfile.h new file mode 100644 index 000000000..660295a05 --- /dev/null +++ b/gtestsuite/src/blis_inpfile.h @@ -0,0 +1,490 @@ +#ifndef BLIS_INPFILE_H +#define BLIS_INPFILE_H + +#include "blis_test.h" + +void libblis_read_inpprms + ( + string str, + test_params_t* params, + test_ops_t* ops, + printres_t* pfr + ); + +void libblis_read_inpops + ( + string ss, + test_params_t* params, + test_ops_t* ops, + string api, + printres_t* pfr + ); + +void libblis_read_api + ( + test_ops_t* ops, + opid_t opid, + dimset_t dimset, + unsigned int n_params, + test_op_t* op + ); + +int libblis_test_read_randv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_randm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_addv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_amaxv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_axpbyv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_axpyv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_copyv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_dotv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_dotxv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_normfv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_scal2v_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_scalv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_setv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_xpbyv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_subv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_axpyf_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_axpy2v_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_dotxf_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_dotaxpyv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_dotxaxpyf_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_addm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_axpym_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_copym_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_normfm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_scal2m_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_scalm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_setm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_subm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_xpbym_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + + +int libblis_test_read_gemv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_ger_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_hemv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_her_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_her2_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_symv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_syr_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_syr2_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_trmv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_trsv_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemmt_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_hemm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_herk_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_her2k_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_symm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_syrk_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_syr2k_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_trmm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_trsm_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_u8s8s32os32_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_u8s8s32os8_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_f32f32f32of32_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_u8s8s32os32_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_u8s8s16os16_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_u8s8s16os8_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_bf16bf16f32of32_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +int libblis_test_read_gemm_bf16bf16f32obf16_params + ( + char* str, + test_op_t* op, + test_params_t* params, + printres_t* pfr + ); + +#endif // BLIS_INPFILE_H + diff --git a/gtestsuite/src/blis_utils.cpp b/gtestsuite/src/blis_utils.cpp new file mode 100644 index 000000000..fd61d459f --- /dev/null +++ b/gtestsuite/src/blis_utils.cpp @@ -0,0 +1,1947 @@ +#include "blis_utils.h" + +using namespace std; + +extern char libblis_test_pass_string[ MAX_PASS_STRING_LENGTH + 1 ]; +extern char libblis_test_warn_string[ MAX_PASS_STRING_LENGTH + 1 ]; +extern char libblis_test_fail_string[ MAX_PASS_STRING_LENGTH + 1 ]; +extern char libblis_test_overflow_string[ MAX_PASS_STRING_LENGTH + 1 ]; +extern char libblis_test_underflow_string[ MAX_PASS_STRING_LENGTH + 1 ]; + +void libblis_test_fprintf_c( FILE* output_stream, const char* message, ... ); +void libblis_test_printf_infoc( const char* message, ... ); + +char libblis_test_binary_name[ MAX_BINARY_NAME_LENGTH + 1 ]; + +void carryover( unsigned int* c, unsigned int* n_vals_for_param, unsigned int n_params ); +void libblis_test_ceil_pow2( obj_t* alpha ); +void libblis_test_parse_message( FILE* output_stream, const char* message, va_list args ) ; + +unsigned int libblis_test_get_n_dims_from_dimset( dimset_t dimset ) ; + +void bli_map_blis_to_netlib_trans( trans_t trans, char* blas_trans ) +{ + if ( trans == BLIS_NO_TRANSPOSE ) *blas_trans = 'N'; + else if ( trans == BLIS_TRANSPOSE ) *blas_trans = 'T'; + else if ( trans == BLIS_CONJ_NO_TRANSPOSE ) *blas_trans = 'C'; + else if ( trans == BLIS_CONJ_TRANSPOSE ) *blas_trans = 'C'; //*blas_trans = 'H' + else + { + bli_check_error_code( BLIS_INVALID_TRANS ); + } +} + +void bli_param_map_char_to_blas_trans( char trans, trans_t* blas_trans ) +{ + if ( trans == 'n' || trans == 'N' ) *blas_trans = BLIS_NO_TRANSPOSE; + else if ( trans == 't' || trans == 'T' ) *blas_trans = BLIS_TRANSPOSE; + else if ( trans == 'c' || trans == 'C' ) *blas_trans = BLIS_CONJ_TRANSPOSE; + else if ( trans == 'h' || trans == 'H' ) *blas_trans = BLIS_CONJ_NO_TRANSPOSE; + else + { + bli_check_error_code( BLIS_INVALID_TRANS ); + } +} + +void bli_param_map_char_to_herk_trans( char trans, trans_t* herk_trans ) +{ + if ( trans == 'n' || trans == 'N' ) *herk_trans = BLIS_NO_TRANSPOSE; + else if ( trans == 'c' || trans == 'C' ) *herk_trans = BLIS_TRANSPOSE; + else + { + bli_check_error_code( BLIS_INVALID_TRANS ); + } +} + +void bli_param_map_char_to_syrk_trans( char trans, trans_t* syrk_trans ) +{ + if ( trans == 'n' || trans == 'N' ) *syrk_trans = BLIS_NO_TRANSPOSE; + else if ( trans == 't' || trans == 'T' ) *syrk_trans = BLIS_TRANSPOSE; + else if ( trans == 'c' || trans == 'C' ) *syrk_trans = BLIS_TRANSPOSE; + else if ( trans == 'h' || trans == 'H' ) *syrk_trans = BLIS_NO_TRANSPOSE; + else + { + bli_check_error_code( BLIS_INVALID_TRANS ); + } +} + +void fill_string_with_n_spaces( char* str, unsigned int n_spaces ) +{ + unsigned int i; + + // Initialze to empty string in case n_spaces == 0. + sprintf( str, "%s", "" ); + + for ( i = 0; i < n_spaces; ++i ) + sprintf( &str[i], " " ); +} + +void libblis_test_build_dims_string(test_op_t* op, tensor_t* dim, char* dims_str) +{ + // For level-1f experiments with fusing factors, we grab the fusing + // factor from the op struct. We do something similar for micro-kernel + // calls. + if ( op->dimset == BLIS_TEST_DIMS_MF ) + { + sprintf( dims_str, " %5u %5u", + ( unsigned int )dim->m, + ( unsigned int ) op->dim_aux[0] ); + } + else if( op->dimset == BLIS_TEST_DIMS_K ) + { + sprintf( dims_str, " %5u %5u %5u", + ( unsigned int ) op->dim_aux[0], + ( unsigned int ) op->dim_aux[1], + ( unsigned int )dim->m ); + } + else if( op->dimset == BLIS_TEST_NO_DIMS ) + { + sprintf( dims_str, " %5u %5u", + ( unsigned int ) op->dim_aux[0], + ( unsigned int ) op->dim_aux[1] ); + } + else // For all other operations, we just use the dim_spec[] values + // and the current problem size. + { + // Initialize the string as empty. + sprintf( dims_str, "%s", "" ); + + // Print all dimensions to a single string. + //for ( i = 0; i < op->n_dims; ++i ) { + if( (op->n_dims > 0) && (dim->m > 0) ) + sprintf( &dims_str[strlen(dims_str)], " %5u", ( unsigned int ) dim->m ); + if( (op->n_dims > 0) && (dim->n > 0) ) + sprintf( &dims_str[strlen(dims_str)], " %5u", ( unsigned int ) dim->n ); + if( (op->n_dims > 0) && (dim->k > 0) ) + sprintf( &dims_str[strlen(dims_str)], " %5u", ( unsigned int ) dim->k ); + } +} + +void libblis_test_read_section_override( test_ops_t* ops, + FILE* input_stream, int* override ) +{ + char buffer[ INPUT_BUFFER_SIZE ]; + + // Read the line for the section override switch. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%d ", override ); +} + +void libblis_test_read_op_info + ( + test_ops_t* ops, + FILE* input_stream, + opid_t opid, + dimset_t dimset, + unsigned int n_params, + test_op_t* op + ) +{ + char buffer[ INPUT_BUFFER_SIZE ]; + char temp[ INPUT_BUFFER_SIZE ]; + unsigned int i, p; + + // Initialize the operation type field. + op->opid = opid; + + // Read the line for the overall operation switch. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%d ", &(op->op_switch) ); + + // Check the op_switch for the individual override value. + if ( op->op_switch == ENABLE_ONLY ) + { + ops->indiv_over = TRUE; + } + + op->n_dims = libblis_test_get_n_dims_from_dimset( dimset ); + op->dimset = dimset; + + if ( op->n_dims > MAX_NUM_DIMENSIONS ) + { + libblis_test_printf_error( "Detected too many dimensions (%u) in input file to store.\n", op->n_dims ); + } + + //printf( "n_dims = %u\n", op->n_dims ); + + // If there is at least one dimension for the current operation, read the + // dimension specifications, which encode the actual dimensions or the + // dimension ratios for each dimension. + if( op->n_dims > 0 ) { + libblis_test_read_next_line( buffer, input_stream ); + + for( i = 0, p = 0; i < op->n_dims; ++i ) + { + //printf( "buffer[p]: %s\n", &buffer[p] ); + + // Advance until we hit non-whitespace (ie: the next number). + for ( ; isspace( buffer[p] ); ++p ) ; + + //printf( "buffer[p] after: %s\n", &buffer[p] ); + + sscanf( &buffer[p], "%d", &(op->dim_spec[i]) ); + + //printf( "dim[%d] = %d\n", i, op->dim_spec[i] ); + + // Advance until we hit whitespace (ie: the space before the next number). + for ( ; !isspace( buffer[p] ); ++p ) ; + } + } + + // If there is at least one parameter for the current operation, read the + // parameter chars, which encode which parameter combinations to test. + if ( n_params > 0 ) + { + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%s ", temp ); + + op->n_params = strlen( temp ); + if ( op->n_params > MAX_NUM_PARAMETERS ) + { + libblis_test_printf_error( "Detected too many parameters (%u) in input file.\n", + op->n_params ); + } + if ( op->n_params != n_params ) + { + libblis_test_printf_error( "Number of parameters specified by caller does not match length of parameter string in input file. strlen( temp ) = %u; n_params = %u\n", op->n_params, n_params ); + } + + strcpy( op->params, temp ); + } + else + { + op->n_params = 0; + strcpy( op->params, "" ); + } + + // Initialize the "test done" switch. + op->test_done = FALSE; + + // Initialize the parent pointer. + op->ops = ops; +} + +void libblis_test_output_section_overrides( FILE* os, test_ops_t* ops ) +{ + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- Section overrides ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "Utility operations %d\n", ops->util_over ); + libblis_test_fprintf_c( os, "Level-1v operations %d\n", ops->l1v_over ); + libblis_test_fprintf_c( os, "Level-1m operations %d\n", ops->l1m_over ); + libblis_test_fprintf_c( os, "Level-1f operations %d\n", ops->l1f_over ); + libblis_test_fprintf_c( os, "Level-2 operations %d\n", ops->l2_over ); + libblis_test_fprintf_c( os, "Level-3 micro-kernels %d\n", ops->l3ukr_over ); + libblis_test_fprintf_c( os, "Level-3 operations %d\n", ops->l3_over ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf( os, "\n" ); +} + +void libblis_test_output_params_struct( FILE* os, test_params_t* params ) +{ + unsigned int i; + //char int_type_size_str[8]; + gint_t int_type_size; + ind_t im; + cntx_t* cntx; + cntx_t* cntx_c; + cntx_t* cntx_z; + + // If bli_info_get_int_type_size() returns 32 or 64, the size is forced. + // Otherwise, the size is chosen automatically. We query the result of + // that automatic choice via sizeof(gint_t). + if ( bli_info_get_int_type_size() == 32 || + bli_info_get_int_type_size() == 64 ) + int_type_size = bli_info_get_int_type_size(); + else + int_type_size = sizeof(gint_t) * 8; + + char impl_str[16]; + char jrir_str[16]; + + // Describe the threading implementation. + if ( bli_info_get_enable_openmp() ) sprintf( impl_str, "openmp" ); + else if ( bli_info_get_enable_pthreads() ) sprintf( impl_str, "pthreads" ); + else /* threading disabled */ sprintf( impl_str, "disabled" ); + + // Describe the status of jrir thread partitioning. + if ( bli_info_get_thread_part_jrir_slab() ) sprintf( jrir_str, "slab" ); + else /*bli_info_get_thread_part_jrir_rr()*/ sprintf( jrir_str, "round-robin" ); + + char nt_str[16]; + char jc_nt_str[16]; + char pc_nt_str[16]; + char ic_nt_str[16]; + char jr_nt_str[16]; + char ir_nt_str[16]; + char api[50]; + + // Query the number of ways of parallelism per loop (and overall) and + // convert these values into strings, with "unset" being used if the + // value returned was -1 (indicating the environment variable was unset). + dim_t nt = bli_thread_get_num_threads(); + dim_t jc_nt = bli_thread_get_jc_nt(); + dim_t pc_nt = bli_thread_get_pc_nt(); + dim_t ic_nt = bli_thread_get_ic_nt(); + dim_t jr_nt = bli_thread_get_jr_nt(); + dim_t ir_nt = bli_thread_get_ir_nt(); + + if ( nt == -1 ) sprintf( nt_str, "unset" ); + else sprintf( nt_str, "%d", ( int ) nt ); + if ( jc_nt == -1 ) sprintf( jc_nt_str, "unset" ); + else sprintf( jc_nt_str, "%d", ( int )jc_nt ); + if ( pc_nt == -1 ) sprintf( pc_nt_str, "unset" ); + else sprintf( pc_nt_str, "%d", ( int )pc_nt ); + if ( ic_nt == -1 ) sprintf( ic_nt_str, "unset" ); + else sprintf( ic_nt_str, "%d", ( int )ic_nt ); + if ( jr_nt == -1 ) sprintf( jr_nt_str, "unset" ); + else sprintf( jr_nt_str, "%d", ( int )jr_nt ); + if ( ir_nt == -1 ) sprintf( ir_nt_str, "unset" ); + else sprintf( ir_nt_str, "%d", ( int )ir_nt ); + + // Set up rntm_t objects for each of the four families: + // gemm, herk, trmm, trsm. + rntm_t gemm, herk, trmm_l, trmm_r, trsm_l, trsm_r; + dim_t m = 1000, n = 1000, k = 1000; + + bli_rntm_init_from_global( &gemm ); + bli_rntm_init_from_global( &herk ); + bli_rntm_init_from_global( &trmm_l ); + bli_rntm_init_from_global( &trmm_r ); + bli_rntm_init_from_global( &trsm_l ); + bli_rntm_init_from_global( &trsm_r ); + + bli_rntm_set_ways_for_op( BLIS_GEMM, BLIS_LEFT, m, n, k, &gemm ); + bli_rntm_set_ways_for_op( BLIS_HERK, BLIS_LEFT, m, n, k, &herk ); + bli_rntm_set_ways_for_op( BLIS_TRMM, BLIS_LEFT, m, n, k, &trmm_l ); + bli_rntm_set_ways_for_op( BLIS_TRMM, BLIS_RIGHT, m, n, k, &trmm_r ); + bli_rntm_set_ways_for_op( BLIS_TRSM, BLIS_LEFT, m, n, k, &trsm_l ); + bli_rntm_set_ways_for_op( BLIS_TRSM, BLIS_RIGHT, m, n, k, &trsm_r ); + + if (params->api == API_CBLAS) + sprintf( api, "cblas" ); + else if(params->api == API_BLAS) + sprintf( api, "blas" ); + else + sprintf( api, "blis" ); + + // Output some system parameters. + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- BLIS library info -------------------------------------\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "version string %s\n", bli_info_get_version_str() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- BLIS configuration info ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "active sub-configuration %s\n", bli_arch_string( bli_arch_query_id() ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "BLIS integer type size (bits) %d\n", ( int )int_type_size ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "Assumed max # of SIMD regs %d\n", ( int )bli_info_get_simd_num_registers() ); + libblis_test_fprintf_c( os, "SIMD size (bytes) %d\n", ( int )bli_info_get_simd_size() ); + libblis_test_fprintf_c( os, "SIMD alignment (bytes) %d\n", ( int )bli_info_get_simd_align_size() ); + libblis_test_fprintf_c( os, "Max stack buffer size (bytes) %d\n", ( int )bli_info_get_stack_buf_max_size() ); + libblis_test_fprintf_c( os, "Page size (bytes) %d\n", ( int )bli_info_get_page_size() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "memory pools\n" ); + libblis_test_fprintf_c( os, " enabled for packing blocks? %d\n", ( int )bli_info_get_enable_pba_pools() ); + libblis_test_fprintf_c( os, " enabled for small blocks? %d\n", ( int )bli_info_get_enable_sba_pools() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "memory alignment (bytes) \n" ); + libblis_test_fprintf_c( os, " stack address %d\n", ( int )bli_info_get_stack_buf_align_size() ); + libblis_test_fprintf_c( os, " obj_t address %d\n", ( int )bli_info_get_heap_addr_align_size() ); + libblis_test_fprintf_c( os, " obj_t stride %d\n", ( int )bli_info_get_heap_stride_align_size() ); + libblis_test_fprintf_c( os, " pool block addr A (+offset) %d (+%d)\n", ( int )bli_info_get_pool_addr_align_size_a(), ( int )bli_info_get_pool_addr_offset_size_a() ); + libblis_test_fprintf_c( os, " pool block addr B (+offset) %d (+%d)\n", ( int )bli_info_get_pool_addr_align_size_b(), ( int )bli_info_get_pool_addr_offset_size_b() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "BLAS/CBLAS compatibility layers \n" ); + libblis_test_fprintf_c( os, " BLAS API enabled? %d\n", ( int )bli_info_get_enable_blas() ); + libblis_test_fprintf_c( os, " CBLAS API enabled? %d\n", ( int )bli_info_get_enable_cblas() ); + libblis_test_fprintf_c( os, " integer type size (bits) %d\n", ( int )bli_info_get_blas_int_type_size() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "libmemkind \n" ); + libblis_test_fprintf_c( os, " enabled? %d\n", ( int )bli_info_get_enable_memkind() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "gemm sandbox \n" ); + libblis_test_fprintf_c( os, " enabled? %d\n", ( int )bli_info_get_enable_sandbox() ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "floating-point types s d c z \n" ); + libblis_test_fprintf_c( os, " sizes (bytes) %7u %7u %7u %7u\n", sizeof(float), + sizeof(double), + sizeof(scomplex), + sizeof(dcomplex) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- BLIS parallelization info ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "multithreading %s\n", impl_str ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "thread auto-factorization \n" ); + libblis_test_fprintf_c( os, " m dim thread ratio %d\n", ( int )BLIS_THREAD_RATIO_M ); + libblis_test_fprintf_c( os, " n dim thread ratio %d\n", ( int )BLIS_THREAD_RATIO_N ); + libblis_test_fprintf_c( os, " jr max threads %d\n", ( int )BLIS_THREAD_MAX_JR ); + libblis_test_fprintf_c( os, " ir max threads %d\n", ( int )BLIS_THREAD_MAX_IR ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "ways of parallelism nt jc pc ic jr ir\n" ); + libblis_test_fprintf_c( os, " environment %5s %5s %5s %5s %5s %5s\n", + nt_str, jc_nt_str, pc_nt_str, + ic_nt_str, jr_nt_str, ir_nt_str ); + libblis_test_fprintf_c( os, " gemm (m,n,k=1000) %5d %5d %5d %5d %5d\n", + ( int )bli_rntm_jc_ways( &gemm ), ( int )bli_rntm_pc_ways( &gemm ), + ( int )bli_rntm_ic_ways( &gemm ), + ( int )bli_rntm_jr_ways( &gemm ), ( int )bli_rntm_ir_ways( &gemm ) ); + libblis_test_fprintf_c( os, " herk (m,k=1000) %5d %5d %5d %5d %5d\n", + ( int )bli_rntm_jc_ways( &herk ), ( int )bli_rntm_pc_ways( &herk ), + ( int )bli_rntm_ic_ways( &herk ), + ( int )bli_rntm_jr_ways( &herk ), ( int )bli_rntm_ir_ways( &herk ) ); + libblis_test_fprintf_c( os, " trmm_l (m,n=1000) %5d %5d %5d %5d %5d\n", + ( int )bli_rntm_jc_ways( &trmm_l ), ( int )bli_rntm_pc_ways( &trmm_l ), + ( int )bli_rntm_ic_ways( &trmm_l ), + ( int )bli_rntm_jr_ways( &trmm_l ), ( int )bli_rntm_ir_ways( &trmm_l ) ); + libblis_test_fprintf_c( os, " trmm_r (m,n=1000) %5d %5d %5d %5d %5d\n", + ( int )bli_rntm_jc_ways( &trmm_r ), ( int )bli_rntm_pc_ways( &trmm_r ), + ( int )bli_rntm_ic_ways( &trmm_r ), + ( int )bli_rntm_jr_ways( &trmm_r ), ( int )bli_rntm_ir_ways( &trmm_r ) ); + libblis_test_fprintf_c( os, " trsm_l (m,n=1000) %5d %5d %5d %5d %5d\n", + ( int )bli_rntm_jc_ways( &trsm_l ), ( int )bli_rntm_pc_ways( &trsm_l ), + ( int )bli_rntm_ic_ways( &trsm_l ), + ( int )bli_rntm_jr_ways( &trsm_l ), ( int )bli_rntm_ir_ways( &trsm_l ) ); + libblis_test_fprintf_c( os, " trsm_r (m,n=1000) %5d %5d %5d %5d %5d\n", + ( int )bli_rntm_jc_ways( &trsm_r ), ( int )bli_rntm_pc_ways( &trsm_r ), + ( int )bli_rntm_ic_ways( &trsm_r ), + ( int )bli_rntm_jr_ways( &trsm_r ), ( int )bli_rntm_ir_ways( &trsm_r ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "thread partitioning \n" ); + //libblis_test_fprintf_c( os, " jc/ic loops %s\n", "slab" ); + libblis_test_fprintf_c( os, " jr/ir loops %s\n", jrir_str ); + libblis_test_fprintf_c( os, "\n" ); + + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- BLIS default implementations ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "level-3 implementations s d c z\n" ); + libblis_test_fprintf_c( os, " gemm %7s %7s %7s %7s\n", + bli_info_get_gemm_impl_string( BLIS_FLOAT ), + bli_info_get_gemm_impl_string( BLIS_DOUBLE ), + bli_info_get_gemm_impl_string( BLIS_SCOMPLEX ), + bli_info_get_gemm_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " hemm %7s %7s %7s %7s\n", + bli_info_get_hemm_impl_string( BLIS_FLOAT ), + bli_info_get_hemm_impl_string( BLIS_DOUBLE ), + bli_info_get_hemm_impl_string( BLIS_SCOMPLEX ), + bli_info_get_hemm_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " herk %7s %7s %7s %7s\n", + bli_info_get_herk_impl_string( BLIS_FLOAT ), + bli_info_get_herk_impl_string( BLIS_DOUBLE ), + bli_info_get_herk_impl_string( BLIS_SCOMPLEX ), + bli_info_get_herk_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " her2k %7s %7s %7s %7s\n", + bli_info_get_her2k_impl_string( BLIS_FLOAT ), + bli_info_get_her2k_impl_string( BLIS_DOUBLE ), + bli_info_get_her2k_impl_string( BLIS_SCOMPLEX ), + bli_info_get_her2k_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " symm %7s %7s %7s %7s\n", + bli_info_get_symm_impl_string( BLIS_FLOAT ), + bli_info_get_symm_impl_string( BLIS_DOUBLE ), + bli_info_get_symm_impl_string( BLIS_SCOMPLEX ), + bli_info_get_symm_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " syrk %7s %7s %7s %7s\n", + bli_info_get_syrk_impl_string( BLIS_FLOAT ), + bli_info_get_syrk_impl_string( BLIS_DOUBLE ), + bli_info_get_syrk_impl_string( BLIS_SCOMPLEX ), + bli_info_get_syrk_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " syr2k %7s %7s %7s %7s\n", + bli_info_get_syr2k_impl_string( BLIS_FLOAT ), + bli_info_get_syr2k_impl_string( BLIS_DOUBLE ), + bli_info_get_syr2k_impl_string( BLIS_SCOMPLEX ), + bli_info_get_syr2k_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trmm %7s %7s %7s %7s\n", + bli_info_get_trmm_impl_string( BLIS_FLOAT ), + bli_info_get_trmm_impl_string( BLIS_DOUBLE ), + bli_info_get_trmm_impl_string( BLIS_SCOMPLEX ), + bli_info_get_trmm_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trmm3 %7s %7s %7s %7s\n", + bli_info_get_trmm3_impl_string( BLIS_FLOAT ), + bli_info_get_trmm3_impl_string( BLIS_DOUBLE ), + bli_info_get_trmm3_impl_string( BLIS_SCOMPLEX ), + bli_info_get_trmm3_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trsm %7s %7s %7s %7s\n", + bli_info_get_trsm_impl_string( BLIS_FLOAT ), + bli_info_get_trsm_impl_string( BLIS_DOUBLE ), + bli_info_get_trsm_impl_string( BLIS_SCOMPLEX ), + bli_info_get_trsm_impl_string( BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, "\n" ); + + //bli_ind_disable_all(); + + bli_ind_oper_enable_only( BLIS_GEMM, BLIS_NAT, BLIS_SCOMPLEX ); + bli_ind_oper_enable_only( BLIS_GEMM, BLIS_NAT, BLIS_DCOMPLEX ); + + libblis_test_fprintf_c( os, "--- BLIS native implementation info ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " c z \n" ); + libblis_test_fprintf_c( os, "complex implementation %7s %7s\n", + bli_ind_oper_get_avail_impl_string( BLIS_GEMM, BLIS_SCOMPLEX ), + bli_ind_oper_get_avail_impl_string( BLIS_GEMM, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, "\n" ); + + // Query a native context. + cntx = bli_gks_query_nat_cntx(); + + libblis_test_fprintf_c( os, "level-3 blocksizes s d c z \n" ); + libblis_test_fprintf_c( os, " mc %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_MC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_MC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_MC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_MC, cntx ) ); + libblis_test_fprintf_c( os, " kc %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_KC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_KC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_KC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_KC, cntx ) ); + libblis_test_fprintf_c( os, " nc %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_NC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_NC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_NC, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_NC, cntx ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " mc maximum %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_FLOAT, BLIS_MC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DOUBLE, BLIS_MC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_MC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_MC, cntx ) ); + libblis_test_fprintf_c( os, " kc maximum %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_FLOAT, BLIS_KC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DOUBLE, BLIS_KC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_KC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_KC, cntx ) ); + libblis_test_fprintf_c( os, " nc maximum %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_FLOAT, BLIS_NC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DOUBLE, BLIS_NC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_NC, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_NC, cntx ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " mr %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_MR, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_MR, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_MR, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_MR, cntx ) ); + libblis_test_fprintf_c( os, " nr %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_NR, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_NR, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_NR, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_NR, cntx ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " mr packdim %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_FLOAT, BLIS_MR, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DOUBLE, BLIS_MR, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_MR, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_MR, cntx ) ); + libblis_test_fprintf_c( os, " nr packdim %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_FLOAT, BLIS_NR, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DOUBLE, BLIS_NR, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_NR, cntx ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_NR, cntx ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "micro-kernel types s d c z\n" ); + libblis_test_fprintf_c( os, " gemm %7s %7s %7s %7s\n", + bli_info_get_gemm_ukr_impl_string( BLIS_NAT, BLIS_FLOAT ), + bli_info_get_gemm_ukr_impl_string( BLIS_NAT, BLIS_DOUBLE ), + bli_info_get_gemm_ukr_impl_string( BLIS_NAT, BLIS_SCOMPLEX ), + bli_info_get_gemm_ukr_impl_string( BLIS_NAT, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " gemmtrsm_l %7s %7s %7s %7s\n", + bli_info_get_gemmtrsm_l_ukr_impl_string( BLIS_NAT, BLIS_FLOAT ), + bli_info_get_gemmtrsm_l_ukr_impl_string( BLIS_NAT, BLIS_DOUBLE ), + bli_info_get_gemmtrsm_l_ukr_impl_string( BLIS_NAT, BLIS_SCOMPLEX ), + bli_info_get_gemmtrsm_l_ukr_impl_string( BLIS_NAT, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " gemmtrsm_u %7s %7s %7s %7s\n", + bli_info_get_gemmtrsm_u_ukr_impl_string( BLIS_NAT, BLIS_FLOAT ), + bli_info_get_gemmtrsm_u_ukr_impl_string( BLIS_NAT, BLIS_DOUBLE ), + bli_info_get_gemmtrsm_u_ukr_impl_string( BLIS_NAT, BLIS_SCOMPLEX ), + bli_info_get_gemmtrsm_u_ukr_impl_string( BLIS_NAT, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trsm_l %7s %7s %7s %7s\n", + bli_info_get_trsm_l_ukr_impl_string( BLIS_NAT, BLIS_FLOAT ), + bli_info_get_trsm_l_ukr_impl_string( BLIS_NAT, BLIS_DOUBLE ), + bli_info_get_trsm_l_ukr_impl_string( BLIS_NAT, BLIS_SCOMPLEX ), + bli_info_get_trsm_l_ukr_impl_string( BLIS_NAT, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trsm_u %7s %7s %7s %7s\n", + bli_info_get_trsm_u_ukr_impl_string( BLIS_NAT, BLIS_FLOAT ), + bli_info_get_trsm_u_ukr_impl_string( BLIS_NAT, BLIS_DOUBLE ), + bli_info_get_trsm_u_ukr_impl_string( BLIS_NAT, BLIS_SCOMPLEX ), + bli_info_get_trsm_u_ukr_impl_string( BLIS_NAT, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "\n" ); + + libblis_test_fprintf_c( os, "--- BLIS induced implementation info ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + + for ( i = 0; i < BLIS_NAT; ++i ) + { + im = (ind_t)i; + if ( params->ind_enable[ im ] == 0 ) continue; + + bli_ind_oper_enable_only( BLIS_GEMM, im, BLIS_SCOMPLEX ); + bli_ind_oper_enable_only( BLIS_GEMM, im, BLIS_DCOMPLEX ); + + //libblis_test_fprintf_c( os, " c z \n" ); + libblis_test_fprintf_c( os, " c z \n" ); + libblis_test_fprintf_c( os, "complex implementation %7s %7s\n", + bli_ind_oper_get_avail_impl_string( BLIS_GEMM, BLIS_SCOMPLEX ), + bli_ind_oper_get_avail_impl_string( BLIS_GEMM, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, "\n" ); + + // Query a native context. + cntx_c = bli_gks_query_ind_cntx( im, BLIS_SCOMPLEX ); + cntx_z = bli_gks_query_ind_cntx( im, BLIS_DCOMPLEX ); + + libblis_test_fprintf_c( os, "level-3 blocksizes c z \n" ); + libblis_test_fprintf_c( os, " mc %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_MC, cntx_c ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_MC, cntx_z ) ); + libblis_test_fprintf_c( os, " kc %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_KC, cntx_c ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_KC, cntx_z ) ); + libblis_test_fprintf_c( os, " nc %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_NC, cntx_c ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_NC, cntx_z ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " mc maximum %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_MC, cntx_c ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_MC, cntx_z ) ); + libblis_test_fprintf_c( os, " kc maximum %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_KC, cntx_c ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_KC, cntx_z ) ); + libblis_test_fprintf_c( os, " nc maximum %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_NC, cntx_c ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_NC, cntx_z ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " mr %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_MR, cntx_c ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_MR, cntx_z ) ); + libblis_test_fprintf_c( os, " nr %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_NR, cntx_c ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_NR, cntx_z ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, " mr packdim %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_MR, cntx_c ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_MR, cntx_z ) ); + libblis_test_fprintf_c( os, " nr packdim %7d %7d\n", + ( int )bli_cntx_get_blksz_max_dt( BLIS_SCOMPLEX, BLIS_NR, cntx_c ), + ( int )bli_cntx_get_blksz_max_dt( BLIS_DCOMPLEX, BLIS_NR, cntx_z ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "micro-kernel types c z\n" ); + libblis_test_fprintf_c( os, " gemm %7s %7s\n", + bli_info_get_gemm_ukr_impl_string( im, BLIS_SCOMPLEX ), + bli_info_get_gemm_ukr_impl_string( im, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " gemmtrsm_l %7s %7s\n", + bli_info_get_gemmtrsm_l_ukr_impl_string( im, BLIS_SCOMPLEX ), + bli_info_get_gemmtrsm_l_ukr_impl_string( im, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " gemmtrsm_u %7s %7s\n", + bli_info_get_gemmtrsm_u_ukr_impl_string( im, BLIS_SCOMPLEX ), + bli_info_get_gemmtrsm_u_ukr_impl_string( im, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trsm_l %7s %7s\n", + bli_info_get_trsm_l_ukr_impl_string( im, BLIS_SCOMPLEX ), + bli_info_get_trsm_l_ukr_impl_string( im, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, " trsm_u %7s %7s\n", + bli_info_get_trsm_u_ukr_impl_string( im, BLIS_SCOMPLEX ), + bli_info_get_trsm_u_ukr_impl_string( im, BLIS_DCOMPLEX ) ); + libblis_test_fprintf_c( os, "\n" ); + + } + + bli_ind_disable_all(); + + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- BLIS misc. other info ---\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "level-2 cache blocksizes s d c z \n" ); + libblis_test_fprintf_c( os, " m dimension %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_M2, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_M2, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_M2, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_M2, cntx ) ); + libblis_test_fprintf_c( os, " n dimension %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_N2, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_N2, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_N2, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_N2, cntx ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "level-1f fusing factors s d c z \n" ); + libblis_test_fprintf_c( os, " axpyf %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_AF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_AF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_AF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_AF, cntx ) ); + libblis_test_fprintf_c( os, " dotxf %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_DF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_DF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_DF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_DF, cntx ) ); + libblis_test_fprintf_c( os, " dotxaxpyf %7d %7d %7d %7d\n", + ( int )bli_cntx_get_blksz_def_dt( BLIS_FLOAT, BLIS_XF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DOUBLE, BLIS_XF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_SCOMPLEX, BLIS_XF, cntx ), + ( int )bli_cntx_get_blksz_def_dt( BLIS_DCOMPLEX, BLIS_XF, cntx ) ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf( os, "\n" ); + + // Output the contents of the param struct. + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "--- BLIS test suite parameters ----------------------------\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf_c( os, "num repeats per experiment %u\n", params->n_repeats ); + libblis_test_fprintf_c( os, "num matrix storage schemes %u\n", params->n_mstorage ); + libblis_test_fprintf_c( os, "storage[ matrix ] %s\n", params->storage[ BLIS_TEST_MATRIX_OPERAND ] ); + libblis_test_fprintf_c( os, "num vector storage schemes %u\n", params->n_vstorage ); + libblis_test_fprintf_c( os, "storage[ vector ] %s\n", params->storage[ BLIS_TEST_VECTOR_OPERAND ] ); + libblis_test_fprintf_c( os, "mix all storage schemes? %u\n", params->mix_all_storage ); + libblis_test_fprintf_c( os, "test with aligned memory? %u\n", params->alignment ); + libblis_test_fprintf_c( os, "randomization method %u\n", params->rand_method ); + libblis_test_fprintf_c( os, "general stride spacing %u\n", params->gs_spacing ); + libblis_test_fprintf_c( os, "num datatypes %u\n", params->n_datatypes ); + libblis_test_fprintf_c( os, "datatype[0] %d (%c)\n", params->datatype[0], + params->datatype_char[0] ); + for( i = 1; i < params->n_datatypes; ++i ) + libblis_test_fprintf_c( os, " [%d] %d (%c)\n", i, params->datatype[i], + params->datatype_char[i] ); + libblis_test_fprintf_c( os, "mix domains for gemm? %u\n", params->mixed_domain ); + libblis_test_fprintf_c( os, "mix precisions for gemm? %u\n", params->mixed_precision ); + libblis_test_fprintf_c( os, "problem size: first to test %u\n", params->p_first ); + libblis_test_fprintf_c( os, "problem size: max to test %u\n", params->p_max ); + libblis_test_fprintf_c( os, "problem size increment %u\n", params->p_inc ); + libblis_test_fprintf_c( os, "complex implementations \n" ); + libblis_test_fprintf_c( os, " 3mh? %u\n", params->ind_enable[ BLIS_3MH ] ); + libblis_test_fprintf_c( os, " 3m1? %u\n", params->ind_enable[ BLIS_3M1 ] ); + libblis_test_fprintf_c( os, " 4mh? %u\n", params->ind_enable[ BLIS_4MH ] ); + libblis_test_fprintf_c( os, " 4m1b (4mb)? %u\n", params->ind_enable[ BLIS_4M1B ] ); + libblis_test_fprintf_c( os, " 4m1a (4m1)? %u\n", params->ind_enable[ BLIS_4M1A ] ); + libblis_test_fprintf_c( os, " 1m? %u\n", params->ind_enable[ BLIS_1M ] ); + libblis_test_fprintf_c( os, " native? %u\n", params->ind_enable[ BLIS_NAT ] ); + libblis_test_fprintf_c( os, "simulated app-level threads %u\n", params->n_app_threads ); + libblis_test_fprintf_c( os, "error-checking level %u\n", params->error_checking_level ); + libblis_test_fprintf_c( os, "reaction to failure %c\n", params->reaction_to_failure ); + libblis_test_fprintf_c( os, "output in matlab format? %u\n", params->output_matlab_format ); + libblis_test_fprintf_c( os, "output to stdout AND files? %u\n", params->output_files ); + libblis_test_fprintf_c( os, "api interface %s\n", api ); + libblis_test_fprintf_c( os, "permutation and combination %s\n", (params->dimf == 1) ? "yes" : "no" ); + libblis_test_fprintf_c( os, "alpha and beta combination %s\n", (params->nab == 0) ? "single value" : "multiple values" ); + libblis_test_fprintf_c( os, "integer bitexact test %s\n", (params->bitextf == 1) ? "enabled" : "disabled" ); + libblis_test_fprintf_c( os, "print cases %s\n", (params->passflag == 1) ? "all" : "only failures" ); + libblis_test_fprintf_c( os, "bit-reproducibility %s\n", (params->bitrp == 1) ? "enabled" : "disabled" ); + libblis_test_fprintf_c( os, "lpgemm memory-format order %s\n", (params->op_t == 'p') ? "no-reorder" : "reorder" ); + libblis_test_fprintf_c( os, "-----------------------------------------------------------\n" ); + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf( os, "\n" ); + +#ifndef BLIS_ENABLE_GEMM_MD + // Notify the user if mixed domain or mixed precision was requested. + if ( params->mixed_domain || params->mixed_precision ) + { + libblis_test_printf_error( "mixed domain and/or mixed precision testing requested, but building against BLIS without mixed datatype support.\n" ); + } +#endif + + // If mixed domain or mixed precision was requested, we disable all + // induced methods except 1m and native execution. + if ( params->mixed_domain || params->mixed_precision ) + { + ind_t im; + + for ( i = BLIS_IND_FIRST; i < BLIS_IND_LAST+1; ++i ) + { + im = (ind_t)i; + if ( im != BLIS_1M && im != BLIS_NAT ) + params->ind_enable[ im ] = 0; + } + } +} + +void libblis_test_output_op_struct( FILE* os, test_op_t* op, char* op_str ) +{ + + dimset_t dimset = op->dimset; + + if ( dimset == BLIS_TEST_DIMS_MNK ) { + libblis_test_fprintf_c( os, "%s m n k %d %d %d\n", op_str, + op->dim_spec[0], op->dim_spec[1], op->dim_spec[2] ); + } + else if ( dimset == BLIS_TEST_DIMS_MN ) { + libblis_test_fprintf_c( os, "%s m n %d %d\n", op_str, + op->dim_spec[0], op->dim_spec[1] ); + } + else if ( dimset == BLIS_TEST_DIMS_MK ) { + libblis_test_fprintf_c( os, "%s m k %d %d\n", op_str, + op->dim_spec[0], op->dim_spec[1] ); + } + else if ( dimset == BLIS_TEST_DIMS_M || + dimset == BLIS_TEST_DIMS_MF ) { + libblis_test_fprintf_c( os, "%s m %d\n", op_str, + op->dim_spec[0] ); + } + else if ( dimset == BLIS_TEST_DIMS_K ) { + libblis_test_fprintf_c( os, "%s k %d\n", op_str, + op->dim_spec[0] ); + } + else if ( dimset == BLIS_TEST_NO_DIMS ) { + // Do nothing. + } + else { + libblis_test_printf_error( "Invalid dimension combination.\n" ); + } + + if ( op->n_params > 0 ) + libblis_test_fprintf_c( os, "%s operand params %s\n", op_str, op->params ); + else + libblis_test_fprintf_c( os, "%s operand params %s\n", op_str, "(none)" ); + + libblis_test_fprintf_c( os, "\n" ); + libblis_test_fprintf( os, "\n" ); +} + +param_t libblis_test_get_param_type_for_char( char p_type ) +{ + param_t r_val; + + if ( p_type == 's' ) r_val = BLIS_TEST_PARAM_SIDE; + else if ( p_type == 'u' ) r_val = BLIS_TEST_PARAM_UPLO; + else if ( p_type == 'e' ) r_val = BLIS_TEST_PARAM_UPLODE; + else if ( p_type == 'h' ) r_val = BLIS_TEST_PARAM_TRANS; + else if ( p_type == 'c' ) r_val = BLIS_TEST_PARAM_CONJ; + else if ( p_type == 'd' ) r_val = BLIS_TEST_PARAM_DIAG; + else { + r_val = BLIS_TEST_PARAM_SIDE; + libblis_test_printf_error( "Invalid parameter character.\n" ); + } + + return r_val; +} + +operand_t libblis_test_get_operand_type_for_char( char o_type ) +{ + operand_t r_val; + + if ( o_type == 'm' ) r_val = BLIS_TEST_MATRIX_OPERAND; + else if ( o_type == 'v' ) r_val = BLIS_TEST_VECTOR_OPERAND; + else { + r_val = BLIS_TEST_MATRIX_OPERAND; + libblis_test_printf_error( "Invalid operand character.\n" ); + } + return r_val; +} + +unsigned int libblis_test_get_n_dims_from_dimset( dimset_t dimset ) +{ + + unsigned int n_dims; + + if ( dimset == BLIS_TEST_DIMS_MNK ) n_dims = 3; + else if ( dimset == BLIS_TEST_DIMS_MN ) n_dims = 2; + else if ( dimset == BLIS_TEST_DIMS_MK ) n_dims = 2; + else if ( dimset == BLIS_TEST_DIMS_M ) n_dims = 1; + else if ( dimset == BLIS_TEST_DIMS_MF ) n_dims = 1; + else if ( dimset == BLIS_TEST_DIMS_K ) n_dims = 1; + else if ( dimset == BLIS_TEST_NO_DIMS ) n_dims = 0; + else { + n_dims = 0; + libblis_test_printf_error( "Invalid dimension combination.\n" ); + } + + return n_dims; +} + +unsigned int libblis_test_get_n_dims_from_string( char* dims_str ) +{ + unsigned int n_dims; + char* cp; + + cp = dims_str; + + for ( n_dims = 0; *cp != '\0'; ++n_dims ) { + while ( isspace( *cp ) ) { + ++cp; + } + + while ( isdigit( *cp ) ) { + ++cp; + } + } + + return n_dims; +} + +dim_t libblis_test_get_dim_from_prob_size( int dim_spec, unsigned int p_size ) +{ + dim_t dim; + + if ( dim_spec < 0 ) + dim = p_size / bli_abs(dim_spec); + else + dim = dim_spec; + + return dim; +} + +void libblis_test_fill_param_strings( char* p_spec_str, + char** chars_for_param, + unsigned int n_params, + unsigned int n_param_combos, + char** pc_str ) +{ + unsigned int pci, pi, i; + unsigned int* counter; + unsigned int* n_vals_for_param; + + // Allocate an array that will store the number of parameter values + // for each parameter. + n_vals_for_param = ( unsigned int* ) malloc( n_params * sizeof( unsigned int ) ); + + // Fill n_vals_for_param[i] with the number of parameter values (chars) + // in chars_for_param[i] (this is simply the string length). + for ( i = 0; i < n_params; ++i ) { + if ( p_spec_str[i] == '?' ) + n_vals_for_param[i] = strlen( chars_for_param[i] ); + else + n_vals_for_param[i] = 1; + } + + // Allocate an array with one digit per parameter. We will use + // this array to keep track of our progress as we canonically move + // though all possible parameter combinations. + counter = ( unsigned int* ) malloc( n_params * sizeof( unsigned int ) ); + + // Initialize all values in c to zero. + for ( i = 0; i < n_params; ++i ) + counter[i] = 0; + + for ( pci = 0; pci < n_param_combos; ++pci ) { + // Iterate backwards through each parameter string we create, since we + // want to form (for example, if the parameters are transa and conjx: + // (1) nn, (2) nc, (3) cn, (4) cc, (5) tn, (6) tc, (7) hn, (8) hc. + for ( i = 0, pi = n_params - 1; i < n_params; --pi, ++i ) { + // If the current parameter character, p_spec_str[pi] is fixed (ie: if + // it is not '?'), then just copy it into the parameter combination + // string. Otherwise, map the current integer value in c to the + // corresponding character in char_for_param[pi]. + if ( p_spec_str[pi] != '?' ) + pc_str[pci][pi] = p_spec_str[pi]; + else + pc_str[pci][pi] = chars_for_param[ pi ][ counter[pi] ]; + } + + // Terminate the current parameter combination string. + pc_str[pci][n_params] = '\0'; + + // Only try to increment/carryover if this is NOT the last param + // combo. + if ( pci < n_param_combos - 1 ) { + // Increment the least-most significant counter. + counter[ n_params - 1 ]++; + + // Perform "carryover" if needed. + carryover( &counter[ n_params - 1 ], &n_vals_for_param[ n_params - 1 ], n_params ); + } + } + + // Free the temporary arrays. + free( counter ); + + // Free the array holding the number of parameter values for each + // parameter. + free( n_vals_for_param ); +} + +void carryover( unsigned int* c, + unsigned int* n_vals_for_param, + unsigned int n_params ) +{ + if ( n_params == 1 ) + return; + else { + if ( *c == *n_vals_for_param ) { + *c = 0; + *(c-1) += 1; + carryover( c-1, n_vals_for_param-1, n_params-1 ); + } + } +} + +void libblis_test_vobj_randomize( test_params_t* params, bool normalize, obj_t* x ) +{ + if( params->rand_method == BLIS_TEST_RAND_REAL_VALUES ) + bli_randv( x ); + else // if ( params->rand_method == BLIS_TEST_RAND_NARROW_POW2 ) + bli_randnv( x ); + + if( normalize ) + { + num_t dt = bli_obj_dt( x ); + num_t dt_r = bli_obj_dt_proj_to_real( x ); + obj_t kappa; + obj_t kappa_r; + + bli_obj_scalar_init_detached( dt, &kappa ); + bli_obj_scalar_init_detached( dt_r, &kappa_r ); + + // Normalize vector elements. The following code ensures that we + // always invert-scale by whole power of two. + bli_normfv( x, &kappa_r ); + libblis_test_ceil_pow2( &kappa_r ); + bli_copysc( &kappa_r, &kappa ); + bli_invertsc( &kappa ); + bli_scalv( &kappa, x ); + } +} + +void libblis_test_mobj_randomize( test_params_t* params, bool normalize, obj_t* a ) +{ + if ( params->rand_method == BLIS_TEST_RAND_REAL_VALUES ) + bli_randm( a ); + else // if ( params->rand_method == BLIS_TEST_RAND_NARROW_POW2 ) + bli_randnm( a ); + + if ( normalize ) + { + num_t dt = bli_obj_dt( a ); + num_t dt_r = bli_obj_dt_proj_to_real( a ); + obj_t kappa; + obj_t kappa_r; + + bli_obj_scalar_init_detached( dt, &kappa ); + bli_obj_scalar_init_detached( dt_r, &kappa_r ); + + // Normalize matrix elements. + bli_norm1m( a, &kappa_r ); + libblis_test_ceil_pow2( &kappa_r ); + bli_copysc( &kappa_r, &kappa ); + bli_invertsc( &kappa ); + bli_scalm( &kappa, a ); + } +} + +void libblis_test_ceil_pow2( obj_t* alpha ) +{ + double alpha_r; + double alpha_i; + + bli_getsc( alpha, &alpha_r, &alpha_i ); + + alpha_r = pow( 2.0, ceil( log2( alpha_r ) ) ); + + bli_setsc( alpha_r, alpha_i, alpha ); +} + +void libblis_test_mobj_load_diag( test_params_t* params, obj_t* a ) +{ + // We assume that all elements of a were intialized on interval [-1,1]. + + // Load the diagonal by 2.0. + bli_shiftd( &BLIS_TWO, a ); +} + +void libblis_test_build_filename_string( const char* prefix_str, + char* op_str, + char* funcname_str ) +{ + sprintf( funcname_str, "%s_%s.m", prefix_str, op_str ); +} + +void libblis_test_fopen_check_stream( char* filename_str, + FILE* stream ) +{ + // Check for success. + if ( stream == NULL ) + { + libblis_test_printf_error( "Failed to open file %s. Check existence (if file is being read), permissions (if file is being overwritten), and/or storage limit.\n", + filename_str ); + } +} + +void libblis_test_fopen_ofile( char* op_str, iface_t iface, FILE** output_stream ) +{ + char filename_str[ MAX_FILENAME_LENGTH ]; + + if ( iface == BLIS_TEST_MT_FRONT_END ) + bli_check_error_code( BLIS_NOT_YET_IMPLEMENTED ); + + // Construct a filename string for the current operation. + libblis_test_build_filename_string( BLIS_FILE_PREFIX_STR, + op_str, + filename_str ); + + // Open the output file (overwriting a previous instance, if it exists) + // for writing (in binary mode). + *output_stream = fopen( filename_str, "wb" ); + + // Check the output stream and report an error if something went wrong. + libblis_test_fopen_check_stream( filename_str, *output_stream ); +} + +void libblis_test_fclose_ofile( FILE* output_stream ) +{ + fclose( output_stream ); +} + +void libblis_test_read_next_line( char* buffer, FILE* input_stream ) +{ + char temp[ INPUT_BUFFER_SIZE ]; + + // We want to read at least one line, so we use a do-while loop. + do { + // Read the next line into a temporary buffer and check success. + if ( fgets( temp, INPUT_BUFFER_SIZE-1, input_stream ) == NULL ) { + if ( feof( input_stream ) ) + libblis_test_printf_error( "Error reading input file: encountered unexpected EOF." ); + else + libblis_test_printf_error( "Error (non-EOF) reading input file." ); + } + // We continue to read lines into buffer until the line is neither + // commented nor blank. + }while ( temp[0] == INPUT_COMMENT_CHAR || temp[0] == '\n' || + temp[0] == ' ' || temp[0] == '\t' ); + + // Save the string in temp, up to first white space character, into buffer. + //sscanf( temp, "%s ", buffer ); + strcpy( buffer, temp ); + + //printf( "libblis_test_read_next_line() read: %s\n", buffer ); +} + +void libblis_test_fprintf( FILE* output_stream, const char* message, ... ) +{ + va_list args; + + // Initialize variable argument environment. + va_start( args, message ); + + // Parse the received message and print its components. + libblis_test_parse_message( output_stream, message, args ); + + // Shutdown variable argument environment and clean up stack. + va_end( args ); + + // Flush the output stream. + fflush( output_stream ); +} + +void libblis_test_fprintf_c( FILE* output_stream, const char* message, ... ) +{ + va_list args; + + fprintf( output_stream, "%c ", OUTPUT_COMMENT_CHAR ); + + // Initialize variable argument environment. + va_start( args, message ); + + // Parse the received message and print its components. + libblis_test_parse_message( output_stream, message, args ); + + // Shutdown variable argument environment and clean up stack. + va_end( args ); + + // Flush the output stream. + fflush( output_stream ); +} + +void libblis_test_printf_info( const char* message, ... ) +{ + FILE* output_stream = stdout; + va_list args; + + // Initialize variable argument environment. + va_start( args, message ); + + // Parse the received message and print its components. + libblis_test_parse_message( output_stream, message, args ); + + // Shutdown variable argument environment and clean up stack. + va_end( args ); + + // Flush the output stream. + fflush( output_stream ); +} + +void libblis_test_printf_infoc( const char* message, ... ) +{ + FILE* output_stream = stdout; + va_list args; + + fprintf( output_stream, "%c ", OUTPUT_COMMENT_CHAR ); + + // Initialize variable argument environment. + va_start( args, message ); + + // Parse the received message and print its components. + libblis_test_parse_message( output_stream, message, args ); + + // Shutdown variable argument environment and clean up stack. + va_end( args ); + + // Flush the output stream. + fflush( output_stream ); +} + +void libblis_test_printf_error( const char* message, ... ) +{ + FILE* output_stream = stderr; + va_list args; + + fprintf( output_stream, "%s: *** error ***: ", libblis_test_binary_name ); + + // Initialize variable argument environment. + va_start( args, message ); + + // Parse the received message and print its components. + libblis_test_parse_message( output_stream, message, args ); + + // Shutdown variable argument environment and clean up stack. + va_end( args ); + + // Flush the output stream. + fflush( output_stream ); + + // Exit. + exit(1); +} + +void libblis_test_parse_message( FILE* output_stream, const char* message, va_list args ) +{ + int c, cf; + char format_spec[8]; + unsigned int the_uint; + int the_int; + double the_double; + char* the_string; + char the_char; + + // Begin looping over message to insert variables wherever there are + // format specifiers. + for ( c = 0; message[c] != '\0'; ) { + if ( message[c] != '%' ) { + fprintf( output_stream, "%c", message[c] ); + c += 1; + } + else if ( message[c] == '%' && message[c+1] == '%' ) {// handle escaped '%' chars. + fprintf( output_stream, "%c", message[c] ); + c += 2; + } + else { + // Save the format string if there is one. + format_spec[0] = '%'; + for ( c += 1, cf = 1; strchr( "udefsc", message[c] ) == NULL; ++c, ++cf ) { + format_spec[cf] = message[c]; + } + + // Add the final type specifier, and null-terminate the string. + format_spec[cf] = message[c]; + format_spec[cf+1] = '\0'; + + // Switch based on type, since we can't predict what will + // va_args() will return. + switch ( message[c] ) { + case 'u': + the_uint = va_arg( args, unsigned int ); + fprintf( output_stream, format_spec, the_uint ); + break; + + case 'd': + the_int = va_arg( args, int ); + fprintf( output_stream, format_spec, the_int ); + break; + + case 'e': + the_double = va_arg( args, double ); + fprintf( output_stream, format_spec, the_double ); + break; + + case 'f': + the_double = va_arg( args, double ); + fprintf( output_stream, format_spec, the_double ); + break; + + case 's': + the_string = va_arg( args, char* ); + fprintf( output_stream, format_spec, the_string ); + break; + + case 'c': + the_char = va_arg( args, int ); + fprintf( output_stream, "%c", the_char ); + break; + } + + // Move to next character past type specifier. + c += 1; + } + } +} + +void libblis_test_check_empty_problem( obj_t* c, double* resid ) +{ + if ( bli_obj_has_zero_dim( c ) ) { + *resid = 0.0; + } +} + +bool libblis_test_op_is_done( test_op_t* op ) +{ + return op->test_done; +} + +int libblis_test_op_is_disabled( test_op_t* op ) +{ + int r_val; + + // If there was at least one individual override, then an op test is + // disabled if it is NOT equal to ENABLE_ONLY. If there were no + // individual overrides, then an op test is disabled if it is equal + // to DISABLE. + if ( op->ops->indiv_over == TRUE ) { + if ( op->op_switch != ENABLE_ONLY ) + r_val = TRUE; + else + r_val = FALSE; + } + else {// if ( op->ops->indiv_over == FALSE ) + if ( op->op_switch == DISABLE ) + r_val = TRUE; + else + r_val = FALSE; + } + return r_val; +} + +char* libblis_test_get_result(double resid, const thresh_t* thresh, + char* dc_str, test_params_t* params ) { + char* r_val; + num_t dt; + bli_param_map_char_to_blis_dt(dc_str[0], &dt ); + + if(params->bitextf == 1 ) { + r_val = libblis_test_pass_string; + double dmx = 0.0; + double dmn = 0.0; + switch( dt ) { + case BLIS_FLOAT : + case BLIS_SCOMPLEX : + { + dmx = (double)(std::numeric_limits::max)();; + dmn = (double)(std::numeric_limits::min)();; + break; + } + case BLIS_DOUBLE : + case BLIS_DCOMPLEX : + { + dmx = (std::numeric_limits::max)(); + dmn = (std::numeric_limits::min)(); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + + if( params->oruflw == BLIS_OVERFLOW ) { + if(( bli_isnan( resid ) || bli_isinf( resid ) || + ( resid > dmx )) && ( resid != 0.0 )) + { + r_val = libblis_test_overflow_string; + } + } + else if( params->oruflw == BLIS_UNDERFLOW ) { + if(( bli_isnan( resid ) || bli_isinf( resid ) || + ( resid < dmn )) && ( resid != 0.0 )) + { + r_val = libblis_test_underflow_string; + } + } + else { /* params->oruflw == BLIS_DEFAULT */ + if ( resid != 0 ) r_val = libblis_test_fail_string; + else r_val = libblis_test_pass_string; + } + } + else if ( bli_isnan( resid ) || bli_isinf( resid ) ) { + r_val = libblis_test_fail_string; + } + else { + // Check the result against the thresholds. + if ( resid > thresh[dt].failwarn ) r_val = libblis_test_fail_string; + else if ( resid > thresh[dt].warnpass ) r_val = libblis_test_warn_string; + else r_val = libblis_test_pass_string; + } + return r_val; +} + +bool libblis_test_get_string_for_result( double resid, num_t dt, + const thresh_t* thresh, char *r_val ) { + bool res; + // Before checking against the thresholds, make sure the residual is + // neither NaN nor Inf. (Note that bli_isnan() and bli_isinf() are + // both simply wrappers to the isnan() and isinf() macros defined + // defined in math.h.) + if ( bli_isnan( resid ) || bli_isinf( resid ) ) { + r_val = libblis_test_fail_string; + res = false; + } + else { + // Check the result against the thresholds. + if ( resid > thresh[dt].failwarn ) { + r_val = libblis_test_fail_string; + res = false; + } + else if ( resid > thresh[dt].warnpass ) { + r_val = libblis_test_warn_string; + res = true; + } + else { + r_val = libblis_test_pass_string; + res = true; + } + } + return res; +} + +int libblis_test_util_is_disabled( test_op_t* op ) { + if ( op->ops->util_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +int libblis_test_l1v_is_disabled( test_op_t* op ) { + if ( op->ops->l1v_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +int libblis_test_l1m_is_disabled( test_op_t* op ) { + if ( op->ops->l1m_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +int libblis_test_l1f_is_disabled( test_op_t* op ) { + if ( op->ops->l1f_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +int libblis_test_l2_is_disabled( test_op_t* op ) +{ + if( op->ops->l2_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +int libblis_test_l3ukr_is_disabled( test_op_t* op ) +{ + if( op->ops->l3ukr_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +int libblis_test_l3_is_disabled( test_op_t* op ) +{ + if( op->ops->l3_over == DISABLE ) + return TRUE; + else + return FALSE; +} + +// --- +int libblis_test_dt_str_has_sp_char_str( int n, char* str ) +{ + for ( int i = 0 ; i < n ; ++i ) + { + if ( str[i] == 's' || str[i] == 'c' ) + return TRUE; + } + return FALSE; +} + +int libblis_test_dt_str_has_sp_char( test_params_t* params ) +{ + return libblis_test_dt_str_has_sp_char_str( params->n_datatypes, + params->datatype_char ); +} + +// --- +int libblis_test_dt_str_has_dp_char_str( int n, char* str ) +{ + for ( int i = 0 ; i < n ; ++i ) + { + if ( str[i] == 'd' || str[i] == 'z' ) + return TRUE; + } + return FALSE; +} + +int libblis_test_dt_str_has_dp_char( test_params_t* params ) +{ + return libblis_test_dt_str_has_dp_char_str( params->n_datatypes, + params->datatype_char ); +} + +// --- +int libblis_test_dt_str_has_rd_char_str( int n, char* str ) +{ + for ( int i = 0; i < n; ++i ) + { + if ( str[i] == 's' || str[i] == 'd' ) + return TRUE; + } + return FALSE; +} + +int libblis_test_dt_str_has_rd_char( test_params_t* params ) { + return libblis_test_dt_str_has_rd_char_str( params->n_datatypes, + params->datatype_char ); +} + +// --- +int libblis_test_dt_str_has_cd_char_str( int n, char* str ) +{ + for ( int i = 0; i < n; ++i ) + { + if ( str[i] == 'c' || str[i] == 'z' ) + return TRUE; + } + return FALSE; +} + +int libblis_test_dt_str_has_cd_char( test_params_t* params ) +{ + return libblis_test_dt_str_has_cd_char_str( params->n_datatypes, + params->datatype_char ); +} + +// --- +unsigned int libblis_test_count_combos ( + unsigned int n_operands, + char* spec_str, + char** char_sets +) +{ + unsigned int n_combos = 1; + + for ( unsigned int i = 0; i < n_operands; ++i ) + { + if ( spec_str[i] == '?' ) + n_combos *= strlen( char_sets[i] ); + } + + return n_combos; +} + +char libblis_test_proj_dtchar_to_precchar( char dt_char ) +{ + char r_val = dt_char; + if ( r_val == 'c' ) + r_val = 's'; + else if( r_val == 'z' ) + r_val = 'd'; + + return r_val; +} + +//////////////////////////////////////////////////////////////////////// +#ifdef __GTESTSUITE_MALLOC_BUFFER__ +void libblis_test_alloc_buffer( obj_t* a ) +{ + dim_t n_elem = 0; + dim_t m, n; + siz_t elem_size; + siz_t buffer_size; + void* p; + inc_t rs,cs,is; + + bli_obj_free( a ); + + // Query the dimensions of the object we are allocating. + m = bli_obj_length( a ); + n = bli_obj_width( a ); + rs = bli_obj_row_stride( a ); + cs = bli_obj_col_stride( a ); + is = bli_obj_imag_stride( a ); + + // Query the size of one element. + elem_size = bli_obj_elem_size( a ); + + // Determine how much object to allocate. + if ( m == 0 || n == 0 ) + { + // For empty objects, set n_elem to zero. Row and column strides + // should remain unchanged (because alignment is not needed). + n_elem = 0; + } + else + { + // The number of elements to allocate is given by the distance from + // the element with the lowest address (usually {0, 0}) to the element + // with the highest address (usually {m-1, n-1}), plus one for the + // highest element itself. + n_elem = (m-1) * bli_abs( rs ) + (n-1) * bli_abs( cs ) + 1; + } + + // Handle the special case where imaginary stride is larger than + // normal. + if ( bli_obj_is_complex( a ) ) + { + // Notice that adding is/2 works regardless of whether the + // imaginary stride is unit, something between unit and + // 2*n_elem, or something bigger than 2*n_elem. + n_elem = bli_abs( is ) / 2 + n_elem; + } + + // Compute the size of the total buffer to be allocated, which includes + // padding if the leading dimension was increased for alignment purposes. + buffer_size = ( siz_t )n_elem * elem_size; + + // Allocate the buffer. + p = malloc( buffer_size ); + + // Set individual fields. + bli_obj_set_buffer( p, a ); + +} +#endif + +void libblis_test_obj_free( obj_t* x ) +{ +#ifdef __GTESTSUITE_MALLOC_BUFFER__ + void* p; + // Don't dereference obj if it is NULL. + if ( x != NULL ) + { + p = (void *)bli_obj_buffer( x ); + free( p ); + } +#else + bli_obj_free( x ); +#endif +} + +void libblis_test_vobj_create( test_params_t* params, num_t dt, char storage, + dim_t m, obj_t* x ) +{ + dim_t gs = params->gs_spacing; + + // Column vector (unit stride) + if ( storage == 'c' ) { + bli_obj_create( dt, m, 1, 1, m, x ); +#ifdef __GTESTSUITE_MALLOC_BUFFER__ + libblis_test_alloc_buffer( x ); +#endif + } + // Row vector (unit stride) + else if ( storage == 'r' ) { + bli_obj_create( dt, 1, m, m, 1, x ); +#ifdef __GTESTSUITE_MALLOC_BUFFER__ + libblis_test_alloc_buffer( x ); +#endif + } + // Column vector (non-unit stride) + else if ( storage == 'j' ) { + bli_obj_create( dt, m, 1, gs, gs*m, x ); +#ifdef __GTESTSUITE_MALLOC_BUFFER__ + libblis_test_alloc_buffer( x ); +#endif + } + // Row vector (non-unit stride) + else if ( storage == 'i' ) { + bli_obj_create( dt, 1, m, gs*m, gs, x ); +#ifdef __GTESTSUITE_MALLOC_BUFFER__ + libblis_test_alloc_buffer( x ); +#endif + } + else { + libblis_test_printf_error( "Invalid storage character: %c\n", storage ); + } + +} + +void libblis_test_mobj_create( test_params_t* params, num_t dt, trans_t trans, + char storage, dim_t m, dim_t n, obj_t* a ) { + + dim_t gs = params->gs_spacing; + bool alignment = params->alignment; + siz_t elem_size = bli_dt_size( dt ); + dim_t m_trans = m; + dim_t n_trans = n; + dim_t rs = 1; // Initialization avoids a compiler warning. + dim_t cs = 1; // Initialization avoids a compiler warning. + + // Apply the trans parameter to the dimensions (if needed). + bli_set_dims_with_trans( trans, m, n, &m_trans, &n_trans ); + + // Compute unaligned strides according to the storage case encoded in + // the storage char, and then align the leading dimension if alignment + // was requested. + if ( storage == 'c' ) + { + rs = 1; + cs = m_trans; + + if ( alignment ) + cs = bli_align_dim_to_size( cs, elem_size, + BLIS_HEAP_STRIDE_ALIGN_SIZE ); + } + else if ( storage == 'r' ) + { + rs = n_trans; + cs = 1; + + if ( alignment ) + rs = bli_align_dim_to_size( rs, elem_size, + BLIS_HEAP_STRIDE_ALIGN_SIZE ); + } + else if ( storage == 'g' ) + { + // We apply (arbitrarily) a column tilt, instead of a row tilt, to + // all general stride cases. + rs = gs; + cs = gs * m_trans; + + if ( alignment ) + cs = bli_align_dim_to_size( cs, elem_size, + BLIS_HEAP_STRIDE_ALIGN_SIZE ); + } + else + { + libblis_test_printf_error( "Invalid storage character: %c\n", storage ); + } + + // Create the object using the dimensions and strides computed above. + bli_obj_create( dt, m_trans, n_trans, rs, cs, a ); + +#ifdef __GTESTSUITE_MALLOC_BUFFER__ + libblis_test_alloc_buffer( a ); +#endif + +} + +double libblis_test_vector_check( test_params_t* params, obj_t* y ) +{ + double resid = 0.0; + num_t dt = bli_obj_dt( y ); + f77_int len = bli_obj_vector_dim( y ); + f77_int incy = bli_obj_vector_inc( y ); + + vflg_t flg = params->oruflw; + + switch( dt ) + { + case BLIS_FLOAT : + { + float* Y = (float*) bli_obj_buffer( y ); + resid = libblis_vector_check_real( flg, len, incy, Y ); + break; + } + case BLIS_DOUBLE : + { + double* Y = (double*) bli_obj_buffer( y ); + resid = libblis_vector_check_real( flg, len, incy, Y ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + resid = libblis_vector_check_cmplx( flg, len, incy, Y ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + resid = libblis_vector_check_cmplx( flg, len, incy, Y ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +double libblis_test_matrix_check( test_params_t* params, obj_t* c ) +{ + dim_t rsc, csc; + double resid = 0.0; + num_t dt = bli_obj_dt( c ); + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + + if( bli_obj_row_stride( c ) == 1 ) + { + rsc = 1; + csc = bli_obj_col_stride( c ); + } + else + { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + vflg_t flg = params->oruflw; + + switch( dt ) + { + case BLIS_FLOAT : + { + float* C = (float*) bli_obj_buffer( c ); + resid = libblis_matrix_check_real( flg, C, M, N, rsc, csc ); + break; + } + case BLIS_DOUBLE : + { + double* C = (double*) bli_obj_buffer( c ); + resid = libblis_matrix_check_real( flg, C, M, N, rsc, csc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* C = (scomplex*) bli_obj_buffer( c ); + resid = libblis_matrix_check_cmplx( flg, C, M, N, rsc, csc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* C = (dcomplex*) bli_obj_buffer( c ); + resid = libblis_matrix_check_cmplx( flg, C, M, N, rsc, csc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +double libblis_test_bitrp_vector( obj_t* x, obj_t* y, num_t dt ) +{ + double resid = 0.0; + f77_int len = bli_obj_vector_dim( x ); + f77_int incy = bli_obj_vector_inc( x ); + + switch( dt ) + { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y ); + resid = computediffrv( len, incy, X, Y ); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y ); + resid = computediffrv( len, incy, X, Y ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + resid = computediffiv( len, incy, X, Y ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + resid = computediffiv( len, incy, X, Y ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +double libblis_test_bitrp_matrix( obj_t* c, obj_t* r, num_t dt ) +{ + dim_t rsc, csc; + double resid = 0.0; + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + + if( bli_obj_row_stride( c ) == 1 ) + { + rsc = 1; + csc = bli_obj_col_stride( c ); + } + else + { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) + { + case BLIS_FLOAT : + { + float* C = (float*) bli_obj_buffer( c ); + float* R = (float*) bli_obj_buffer( r ); + resid = computediffrm( M, N, C, R, rsc, csc ); + break; + } + case BLIS_DOUBLE : + { + double* C = (double*) bli_obj_buffer( c ); + double* R = (double*) bli_obj_buffer( r ); + resid = computediffrm( M, N, C, R, rsc, csc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* C = (scomplex*) bli_obj_buffer( c ); + scomplex* R = (scomplex*) bli_obj_buffer( r ); + resid = computediffim( M, N, C, R, rsc, csc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* C = (dcomplex*) bli_obj_buffer( c ); + dcomplex* R = (dcomplex*) bli_obj_buffer( r ); + resid = computediffim( M, N, C, R, rsc, csc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +void conjugate_tensor( obj_t* aa, num_t dt ) +{ + dim_t rs, cs; + dim_t m = bli_obj_length( aa ); + dim_t n = bli_obj_width( aa ); + rs = bli_obj_row_stride( aa ) ; + cs = bli_obj_col_stride( aa ) ; + + switch( dt ) + { + case BLIS_FLOAT : + break; + case BLIS_DOUBLE : + break; + case BLIS_SCOMPLEX : + { + scomplex* aap = (scomplex*) bli_obj_buffer( aa ); + conjugatematrix( aap, m, n, rs, cs ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* aap = (dcomplex*) bli_obj_buffer( aa ); + conjugatematrix( aap, m, n, rs, cs ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return; +} diff --git a/gtestsuite/src/blis_utils.h b/gtestsuite/src/blis_utils.h new file mode 100644 index 000000000..0321cde42 --- /dev/null +++ b/gtestsuite/src/blis_utils.h @@ -0,0 +1,503 @@ +#ifndef BLIS_UTILS_H +#define BLIS_UTILS_H + +#include +#include + +#include "blis_test.h" + +using namespace std; + +#define abscomplex(x) (abs(x.real) + abs(x.imag)) + +#define mulr(x,y) (( x.real * y.real ) - ( x.imag * y.imag )) +#define muli(x,y) (( x.real * y.imag ) + ( x.imag * y.real )) + +#define ELD 8 +#define PAT32 0x78abcdef +#define PAT64 0x0123456789abcdef + +template +T real( T x ) +{ + T r = { 0.0, 0.0 }; + r.real = x.real; + r.imag = 0; + return r; +} + +template +T conjugate( T x ) +{ + T r = { 0.0, 0.0 }; + r.real = x.real; + r.imag = -(x.imag); + return r; +} + +template +T addc( T xx, T yy ) { + T r = { 0.0, 0.0 }; + r.real = xx.real + yy.real; + r.imag = xx.imag + yy.imag; + return r; +} + +template +T subc( T xx, T yy ) +{ + T r = { 0.0, 0.0 }; + r.real = xx.real - yy.real; + r.imag = xx.imag - yy.imag; + return r; +} + +template +T mulc( T xx, T yy ) +{ + T r = { 0.0, 0.0 }; + r.real = mulr( xx, yy ); + r.imag = muli( xx, yy ); + return r; +} + +template +T divc( T yy, T xx ) +{ + T r = { 0.0, 0.0 }; + U s = bli_fmaxabs( (xx.real),(xx.imag) ); + U xxrs = ( (xx.real)/s ); + U xxis = ( (xx.imag)/s ); + U deno = ( (xxrs * xx.real) + (xxis * xx.imag) ); + r.real = ( ((yy.real * xxrs) + (yy.imag * xxis))/deno ); + r.imag = ( ((yy.imag * xxrs) - (yy.real * xxis))/deno ); + return r; +} + +template +T divct( T yy, T xx ) +{ + T r = { 0.0, 0.0 }; + U deno = ( (xx.real * xx.real) + (xx.imag * xx.imag) ); + r.real = ( ((xx.real * yy.real) + (xx.imag * yy.imag))/deno ); + r.imag = ( ((yy.imag * xx.real) - (yy.real * xx.imag))/deno ); + return r; +} + +template +double computediffrv( dim_t len, dim_t incy, T *act, T *ref ) +{ + double resid = 0.0; + unsigned int j,jy = 0; + for( j = 0 ; j < len ; j++ ) + { + auto av = ref[jy]; + auto xc = act[jy]; + resid += xc - av; + jy = jy + incy; + } + return abs(resid); +} + +template +double computediffiv( dim_t len, dim_t incy, T *act, T *ref ) +{ + double resid = 0.0; + unsigned int j,jy = 0; + double rr,ri; + rr = ri = 0.0; + for( j = 0 ; j < len ; j++ ) + { + auto av = ref[jy]; + auto xc = act[jy]; + rr += xc.real - av.real; + ri += xc.imag - av.imag; + jy = jy + incy; + } + resid = rr + ri; + return abs(resid); +} + +template +double computediffrm( dim_t m,dim_t n, T *act, T *ref, dim_t rsc, dim_t csc ) +{ + double resid = 0.0; + unsigned int i,j; + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + auto av = ref[ i*rsc + j*csc ]; + auto xc = act[ i*rsc + j*csc ]; + resid += xc - av; + } + } + return abs(resid); +} + +template +double computediffim( dim_t m,dim_t n, T *act, T *ref, dim_t rsc, dim_t csc ) +{ + unsigned int i,j; + double rr,ri; + double resid = 0.0; + rr = ri = 0.0; + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + auto av = ref[ i*rsc + j*csc ]; + auto xc = act[ i*rsc + j*csc ]; + rr += xc.real - av.real; + ri += xc.imag - av.imag; + } + } + resid = rr + ri; + return abs(resid); +} + +template +double libblis_vector_check_real( vflg_t flg, dim_t len, dim_t incy, T *buf ) +{ + double resid = 0.0; + unsigned int j,jy=0; + T val = 0.0; + if(flg == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + for( j = 0 ; j < len ; j++ ) + { + auto res = buf[jy]; + if((isnan(res)) || (fabs(res) > val)) + { + return abs(res); + } + jy = jy + incy; + } + } + else + { + val = (std::numeric_limits::min)(); + for( j = 0 ; j < len ; j++ ) { + auto res = buf[jy]; + if((isnan(res)) || (fabs(res) < val)) + { + return abs(res); + } + jy = jy + incy; + } + } + return resid; +} + +template +double libblis_vector_check_cmplx( vflg_t flg, dim_t len, dim_t incy, T *buf ) +{ + double resid = 0.0; + unsigned int j,jy=0; + U val = 0.0; + if(flg == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + for( j = 0 ; j < len ; j++ ) + { + auto res = buf[jy]; + if((isnan(res.real) || (isnan(res.imag))) || + (fabs(res.real) > val) || (fabs(res.imag) > val)) + { + resid = (fabs(res.real) > fabs(res.imag)) ? res.real : res.imag; + return abs(resid); + } + jy = jy + incy; + } + } + else + { + val = (std::numeric_limits::min)(); + for( j = 0 ; j < len ; j++ ) + { + auto res = buf[jy]; + if((isnan(res.real) || (isnan(res.imag))) || + (fabs(res.real) < val) || (fabs(res.imag) < val)) + { + resid = (fabs(res.real) < fabs(res.imag)) ? res.imag : res.real; + return abs(resid); + } + jy = jy + incy; + } + } + return resid; +} + +template +double libblis_matrix_check_real( vflg_t flg, T* buf, dim_t m, dim_t n, + dim_t rsc, dim_t csc ) +{ + double resid = 0.0; + unsigned int i,j; + T val = 0.0; + if(flg == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + auto res = buf[ i*rsc + j*csc ]; + if((isnan(res)) || (fabs(res) > val)) + { + return abs(res); + } + } + } + } + else + { + val = (std::numeric_limits::min)(); + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + auto res = buf[ i*rsc + j*csc ]; + if((isnan(res)) || (fabs(res) < val)) + { + return abs(res); + } + } + } + } + return resid; +} + +template +double libblis_matrix_check_cmplx(vflg_t flg, T* buf, dim_t m, dim_t n, + dim_t rsc, dim_t csc) +{ + double resid = 0.0; + unsigned int i,j; + U val = 0.0; + if(flg == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + auto res = buf[ i*rsc + j*csc ]; + if((isnan(res.real) || (isnan(res.imag))) || + (fabs(res.real) > val) || (fabs(res.imag) > val)) + { + resid = (fabs(res.real) > fabs(res.imag)) ? res.real : res.imag; + return abs(resid); + } + } + } + } + else + { + val = (std::numeric_limits::min)(); + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + auto res = buf[ i*rsc + j*csc ]; + if((isnan(res.real) || (isnan(res.imag))) || + (fabs(res.real) < val) || (fabs(res.imag) < val)) + { + resid = (fabs(res.real) < fabs(res.imag)) ? res.imag : res.real; + return abs(resid); + } + } + } + } + return resid; +} + +template +void conjugatematrix(T* X, dim_t m, dim_t n, dim_t rs, dim_t cs) +{ + dim_t i,j; + for( i = 0 ; i < m ; i++ ) + { + for( j = 0 ; j < n ; j++ ) + { + X[i*rs + j*cs] = conjugate( X[i*rs + j*cs] ); + } + } + return; +} + + +template +void test_mmfill( T* dst, T* src, f77_int m, f77_int n, f77_int ld, T val ) +{ + f77_int i,j; + f77_int ldm = ld-ELD; + if( n == ldm ) + { + f77_int tmp; + tmp = n; + n = m; + m = tmp; + } + + for( j = 0 ; j < (n+ELD) ; j++ ) { + for( i = 0 ; i < (m+ELD) ; i++ ) { + dst[ i + j*ld ] = val; + } + } + + for( j = 0 ; j < n ; j++ ) { + for( i = 0 ; i < m ; i++ ) { + dst[ i + j*ld ] = src[ i + j*ldm ]; + } + } +/* + for( j = 0 ; j < n ; j++ ) { + for( i = m ; i < (m+ELD) ; i++ ) { + dst[ i + j*ld ] = val; + } + } + + for( j = n ; j < (n+ELD) ; j++ ) { + for( i = 0 ; i < (m+ELD) ; i++ ) { + dst[ i + j*ld ] = val; + } + } +*/ +} + +template +double test_mmchk( T* dst, f77_int m, f77_int n, f77_int ld, T val ) +{ + f77_int i,j; + f77_int ldm = ld-ELD; + + if( n == ldm ) + { + f77_int tmp; + tmp = n; + n = m; + m = tmp; + } + + for( j = 0 ; j < n ; j++ ) { + for( i = m ; i < (m+ELD) ; i++ ) { + if( dst[ i + j*ld ] != val ) { + cout << "Invalid Access" << endl; + return val; + } + } + } + + for( j = n ; j < (n+ELD) ; j++ ) { + for( i = 0 ; i < (m+ELD) ; i++ ) { + if( dst[ i + j*ld ] != val ) { + cout << "Invalid Access" << endl; + return val; + } + } + } + return 0; +} + +template +double test_mmchkc( T* dst, f77_int m, f77_int n, f77_int ld, U val ) +{ + f77_int i,j; + f77_int ldm = ld-ELD; + + if( n == ldm ) + { + f77_int tmp; + tmp = n; + n = m; + m = tmp; + } + + for( j = 0 ; j < n ; j++ ) { + for( i = m ; i < (m+ELD) ; i++ ) { + T tmp = dst[ i + j*ld ]; + if((tmp.real != val) ||(tmp.imag != val)) { + cout << "Invalid Access" << endl; + return val; + } + } + } + + for( j = n ; j < (n+ELD) ; j++ ) { + for( i = 0 ; i < (m+ELD) ; i++ ) { + T tmp = dst[ i + j*ld ]; + if((tmp.real != val) ||(tmp.imag != val)) { + cout << "Invalid Access" << endl; + return val; + } + } + } + return 0; +} + +void conjugate_tensor( obj_t* aa, num_t dt ); +void libblis_test_build_col_labels_string( test_params_t* params, + test_op_t* op, char* l_str ); +unsigned int libblis_test_get_n_dims_from_dimset( dimset_t dimset ) ; +void bli_param_map_char_to_blas_trans( char trans, trans_t* blas_trans ); +void bli_param_map_char_to_herk_trans( char trans, trans_t* herk_trans ); +void bli_param_map_char_to_syrk_trans( char trans, trans_t* syrk_trans ); +void libblis_test_fprintf( FILE* output_stream, const char* message, ... ); +ind_t ind_enable_get_str( test_params_t*, unsigned int d, unsigned int x, + test_op_t* op ); + +double libblis_test_vector_check( test_params_t* params, obj_t* y ); +double libblis_test_matrix_check( test_params_t* params, obj_t* y ); +double libblis_test_bitrp_vector( obj_t* c, obj_t* r, num_t dt ); +double libblis_test_bitrp_matrix( obj_t* c, obj_t* r, num_t dt ); +void libblis_test_mobj_irandomize( test_params_t* params, obj_t* x ); +void libblis_test_vobj_irandomize( test_params_t* params, obj_t* x ); +void test_fillbuffmem( obj_t* c, num_t dt ); +void test_fillbuffmem_diag( obj_t* c, num_t dt ); + +int libblis_test_dt_str_has_sp_char_str( int n, char* str ); +int libblis_test_dt_str_has_dp_char_str( int n, char* str ); +int libblis_test_dt_str_has_rd_char_str( int n, char* str ); + +void bli_map_blis_to_netlib_trans( trans_t trans, char* blas_trans ); +bool libblis_test_op_is_done( test_op_t* op ); +int libblis_test_l3_is_disabled( test_op_t* op ); +bool libblis_test_get_string_for_result( double resid, num_t dt, + const thresh_t* thresh, char *r_val ); + +void libblis_test_read_next_line( char* buffer, FILE* input_stream ); +void libblis_test_fopen_check_stream( char* filename_str, FILE* stream ); +void libblis_test_read_section_override( test_ops_t* ops, + FILE* input_stream, int* override ); +void libblis_test_read_op_info( test_ops_t* ops, FILE* input_stream, + opid_t opid, dimset_t dimset, unsigned int n_params, test_op_t* op ); +void libblis_test_output_section_overrides( FILE* os, test_ops_t* ops ); +void libblis_test_output_params_struct( FILE* os, test_params_t* params ); + +param_t libblis_test_get_param_type_for_char( char p_type ); +unsigned int libblis_test_count_combos ( unsigned int n_operands, char* spec_str, char** char_sets ); +void libblis_test_fill_param_strings( char* p_spec_str, char** chars_for_param, + unsigned int n_params, unsigned int n_param_combos, char** pc_str ); +operand_t libblis_test_get_operand_type_for_char( char o_type ) ; +int libblis_test_dt_str_has_rd_char( test_params_t* params ); +int libblis_test_dt_str_has_cd_char( test_params_t* params ); +int libblis_test_dt_str_has_sp_char( test_params_t* params ); +int libblis_test_dt_str_has_dp_char( test_params_t* params ); +char libblis_test_proj_dtchar_to_precchar( char dt_char ); + +void libblis_test_printf_error( const char* message, ... ); +void libblis_test_check_empty_problem( obj_t* c, double* resid ); +void libblis_test_mobj_create( test_params_t* params, num_t dt, trans_t trans, + char storage, dim_t m, dim_t n, obj_t* a ); +void libblis_test_vobj_create( test_params_t* params, num_t dt, + char storage, dim_t m, obj_t* x ); +void libblis_test_mobj_randomize( test_params_t* params, bool normalize, obj_t* a ); +void libblis_test_mobj_load_diag( test_params_t* params, obj_t* a ); +void libblis_test_vobj_randomize( test_params_t* params, bool normalize, obj_t* x ); + +void libblis_test_alloc_buffer( obj_t* a ); +void libblis_test_obj_free( obj_t* a ); + +#endif \ No newline at end of file diff --git a/gtestsuite/src/blis_utils_int.cpp b/gtestsuite/src/blis_utils_int.cpp new file mode 100644 index 000000000..17c479302 --- /dev/null +++ b/gtestsuite/src/blis_utils_int.cpp @@ -0,0 +1,625 @@ +#include +#include "blis_utils.h" + +//#define DEF_SRAND + +void bli_isrands( float* alpha ) +{ + /* 24*24*k < 23 bits max value to avoid + rounding off errors */ + int32_t a = ( int32_t ) (rand() % 4); + *alpha = ( float ) a ; +} + +void bli_idrands( double* alpha ) +{ + int64_t a = ( int64_t ) (rand() % 4); + *alpha = ( double ) a ; +} + +void bli_icrands( scomplex* alpha ) +{ + bli_isrands( &(alpha->real) ); + bli_isrands( &(alpha->imag) ); +} + +void bli_izrands( dcomplex* alpha ) +{ + bli_idrands( &(alpha->real) ); + bli_idrands( &(alpha->imag) ); +} + +void bli_israndv( int n, float* x, int incx ) +{ + float* chi; + int i; +#ifdef DEF_SRAND + srand(time(0)); +#endif + for ( i = 0; i < n; ++i ) { + chi = x + i*incx; + bli_isrands( chi ); + } +} + +void bli_idrandv( int n, double* x, int incx ) +{ + double* chi; + int i; +#ifdef DEF_SRAND + srand(time(0)); +#endif + for ( i = 0; i < n; ++i ) + { + chi = x + i*incx; + bli_idrands( chi ); + } +} + +void bli_icrandv( int n, scomplex* x, int incx ) +{ + scomplex* chi; + int i; +#ifdef DEF_SRAND + srand(time(0)); +#endif + for ( i = 0; i < n; ++i ) + { + chi = x + i*incx; + bli_icrands( chi ); + } +} + +void bli_izrandv( int n, dcomplex* x, int incx ) +{ + dcomplex* chi; + int i; +#ifdef DEF_SRAND + srand(time(0)); +#endif + for ( i = 0; i < n; ++i ) + { + chi = x + i*incx; + bli_izrands( chi ); + } +} + + +void bli_israndm(test_params_t* params, int m, int n, float* a, int a_rs, int a_cs ) +{ + float* a_begin, *x; + inc_t inca, lda; + inc_t n_iter; + inc_t n_elem; + int i,j; + + // Return early if possible. + if ( bli_zero_dim2( m, n ) ) return; + + // Initialize with optimal values for column-major storage. + inca = a_rs; + lda = a_cs; + n_iter = n; + n_elem = m; + + // An optimization: if A is row-major, then let's access the matrix by + // rows instead of by columns for increased spatial locality. + if ( bli_is_row_stored( a_rs, a_cs ) ) + { + bli_swap_incs( &n_iter, &n_elem ); + bli_swap_incs( &lda, &inca ); + } + + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + for ( j = 0; j < n_iter; j++ ) + { + a_begin = a + j*lda; + bli_israndv( n_elem, a_begin, inca ); + } + } + else + { + float val; + val = (std::numeric_limits::max)(); + if(params->oruflw == BLIS_UNDERFLOW) + { + val = (std::numeric_limits::min)(); + } + for ( j = 0; j < n_iter; j++ ) + { + x = a + j*lda; + for ( i = 0; i < n_elem; ++i ) + { + x[i*inca] = val ; + } + } + } +} + +void bli_idrandm(test_params_t* params, int m, int n, double* a, int a_rs, int a_cs ) +{ + double* a_begin, *x; + inc_t inca, lda; + inc_t n_iter; + inc_t n_elem; + int i,j; + + // Return early if possible. + if ( bli_zero_dim2( m, n ) ) return; + + // Initialize with optimal values for column-major storage. + inca = a_rs; + lda = a_cs; + n_iter = n; + n_elem = m; + + // An optimization: if A is row-major, then let's access the matrix by + // rows instead of by columns for increased spatial locality. + if ( bli_is_row_stored( a_rs, a_cs ) ) + { + bli_swap_incs( &n_iter, &n_elem ); + bli_swap_incs( &lda, &inca ); + } + + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + for ( j = 0; j < n_iter; j++ ) + { + a_begin = a + j*lda; + bli_idrandv( n_elem, a_begin, inca ); + } + } + else + { + double val; + val = (std::numeric_limits::max)(); + if(params->oruflw == BLIS_UNDERFLOW) + { + val = (std::numeric_limits::min)(); + } + for ( j = 0; j < n_iter; j++ ) { + x = a + j*lda; + for ( i = 0; i < n_elem; ++i ) { + x[i*inca] = val ; + } + } + } +} + +void bli_icrandm(test_params_t* params, int m, int n, scomplex* a, int a_rs, int a_cs ) +{ + scomplex* a_begin, *x; + inc_t inca, lda; + inc_t n_iter; + inc_t n_elem; + int i,j; + + // Return early if possible. + if ( bli_zero_dim2( m, n ) ) return; + + // Initialize with optimal values for column-major storage. + inca = a_rs; + lda = a_cs; + n_iter = n; + n_elem = m; + + // An optimization: if A is row-major, then let's access the matrix by + // rows instead of by columns for increased spatial locality. + if ( bli_is_row_stored( a_rs, a_cs ) ) + { + bli_swap_incs( &n_iter, &n_elem ); + bli_swap_incs( &lda, &inca ); + } + + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + for ( j = 0; j < n_iter; j++ ) + { + a_begin = a + j*lda; + bli_icrandv( n_elem, a_begin, inca ); + } + } + else + { + float val; + val = (std::numeric_limits::max)(); + if(params->oruflw == BLIS_UNDERFLOW) + { + val = (std::numeric_limits::min)(); + } + scomplex cval = {val, val}; + for ( j = 0; j < n_iter; j++ ) + { + x = a + j*lda; + for ( i = 0; i < n_elem; ++i ) + { + x[i*inca] = cval ; + } + } + } +} + +void bli_izrandm(test_params_t* params, int m, int n, dcomplex* a, int a_rs, int a_cs ) { + dcomplex* a_begin, *x; + inc_t inca, lda; + inc_t n_iter; + inc_t n_elem; + int i,j; + + // Return early if possible. + if ( bli_zero_dim2( m, n ) ) return; + + // Initialize with optimal values for column-major storage. + inca = a_rs; + lda = a_cs; + n_iter = n; + n_elem = m; + + // An optimization: if A is row-major, then let's access the matrix by + // rows instead of by columns for increased spatial locality. + if ( bli_is_row_stored( a_rs, a_cs ) ) + { + bli_swap_incs( &n_iter, &n_elem ); + bli_swap_incs( &lda, &inca ); + } + + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + for ( j = 0; j < n_iter; j++ ) + { + a_begin = a + j*lda; + bli_izrandv( n_elem, a_begin, inca ); + } + } + else + { + double val; + val = (std::numeric_limits::max)(); + if(params->oruflw == BLIS_UNDERFLOW) + { + val = (std::numeric_limits::min)(); + } + dcomplex cval = {val, val}; + for ( j = 0; j < n_iter; j++ ) + { + x = a + j*lda; + for ( i = 0; i < n_elem; ++i ) + { + x[i*inca] = cval ; + } + } + } +} + +void libblis_test_mobj_irandomize(test_params_t* params, obj_t* x ) +{ + num_t dt = bli_obj_dt( x ); + dim_t m = bli_obj_length( x ); + dim_t n = bli_obj_width( x ); + inc_t rs = bli_obj_row_stride( x ); + inc_t cs = bli_obj_col_stride( x ); + + switch( dt ) + { + case BLIS_FLOAT : + { + float *buff = ( float * ) bli_obj_buffer_at_off( x ); + bli_israndm(params, m, n, buff, rs, cs ); + break; + } + case BLIS_DOUBLE : + { + double *buff = ( double * ) bli_obj_buffer_at_off( x ); + bli_idrandm(params, m, n, buff, rs, cs ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex *buff = ( scomplex * ) bli_obj_buffer_at_off( x ); + bli_icrandm(params, m, n, buff, rs, cs ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex *buff = ( dcomplex * ) bli_obj_buffer_at_off( x ); + bli_izrandm(params, m, n, buff, rs, cs ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_test_vobj_irandomize(test_params_t* params, obj_t* x ) +{ + num_t dt = bli_obj_dt( x ); + dim_t n = bli_obj_vector_dim( x ); + inc_t inx = bli_obj_vector_inc( x ); + int i; + + switch( dt ) + { + case BLIS_FLOAT : + { + float *buff = ( float * ) bli_obj_buffer_at_off( x ); + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + bli_israndv( n, buff, inx ); + } + else + { + float val; + if(params->oruflw == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + } + else + { + val = (std::numeric_limits::min)(); + } + for ( i = 0; i < n; ++i ) + { + buff[i*inx] = val ; + } + } + break; + } + case BLIS_DOUBLE : + { + double *buff = ( double * ) bli_obj_buffer_at_off( x ); + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + bli_idrandv( n, buff, inx ); + } + else + { + double val; + if(params->oruflw == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + } + else + { + val = (std::numeric_limits::min)(); + } + for ( i = 0; i < n; ++i ) + { + buff[i*inx] = val ; + } + } + break; + } + case BLIS_SCOMPLEX : + { + scomplex *buff = ( scomplex * ) bli_obj_buffer_at_off( x );; + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + bli_icrandv( n, buff, inx ); + } + else + { + float val; + if(params->oruflw == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + } + else + { + val = (std::numeric_limits::min)(); + } + scomplex cval = {val, val}; + for ( i = 0; i < n; ++i ) + { + buff[i*inx] = cval ; + } + } + break; + } + case BLIS_DCOMPLEX : + { + dcomplex *buff = ( dcomplex * ) bli_obj_buffer_at_off( x ); + if(1) //if(params->oruflw == BLIS_DEFAULT) + { + bli_izrandv( n, buff, inx ); + } + else + { + double val; + if(params->oruflw == BLIS_OVERFLOW) + { + val = (std::numeric_limits::max)(); + } + else + { + val = (std::numeric_limits::min)(); + } + dcomplex cval = {val, val}; + for ( i = 0; i < n; ++i ) + { + buff[i*inx] = cval ; + } + } + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +using namespace std; +template +void fillcbuff( dim_t rsc, dim_t csc, obj_t* c ) +{ + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + T* C = (T*) bli_obj_buffer( c ); + T Nan =(T)NAN; + + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + C[ i*rsc + j*csc ] = ( Nan ); + } + } + return; +} + +template +void fillicbuff ( dim_t rsc, dim_t csc, obj_t* c ) +{ + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + U* C = (U*) bli_obj_buffer( c ); + T Nan =(T)NAN; + + U tv = {0,0}; + tv.real = Nan; + tv.imag = Nan; + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + C[ i*rsc + j*csc ] = tv ; + } + } + return; +} + +void test_fillbuffmem(obj_t* c, num_t dt ) +{ + dim_t rsc, csc; + + if( bli_obj_row_stride( c ) == 1 ) + { + rsc = 1; + csc = bli_obj_col_stride( c ); + } + else + { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) + { + case BLIS_FLOAT : + { + fillcbuff( rsc, csc, c ); + break; + } + case BLIS_DOUBLE : + { + fillcbuff( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX : + { + fillicbuff( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX : + { + fillicbuff( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return ; +} + +/////////////////////////////////////////////////////////////////////////////// +using namespace std; +template +void fillcbuff_diag( dim_t rsc, dim_t csc, obj_t* c ) +{ + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + T* C = (T*) bli_obj_buffer( c ); + T val = (T) 2.0; + + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + if(i == j) + { + C[ i*rsc + j*csc ] = ( val ); + } + } + } + return; +} + +template +void fillicbuff_diag ( dim_t rsc, dim_t csc, obj_t* c ) +{ + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + T* C = (T*) bli_obj_buffer( c ); + + T val = {2.0,2.0}; + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + if(i == j) + { + C[ i*rsc + j*csc ] = ( val ); + } + } + } + return; +} + +void test_fillbuffmem_diag( obj_t* c, num_t dt ) +{ + dim_t rsc, csc; + + if( bli_obj_row_stride( c ) == 1 ) + { + rsc = 1; + csc = bli_obj_col_stride( c ); + } + else + { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) + { + case BLIS_FLOAT : + { + fillcbuff_diag( rsc, csc, c ); + break; + } + case BLIS_DOUBLE : + { + fillcbuff_diag( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX : + { + fillicbuff_diag( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX : + { + fillicbuff_diag( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return ; +} +/////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gtestsuite/src/gtest_pthread.cpp b/gtestsuite/src/gtest_pthread.cpp new file mode 100644 index 000000000..4f70a9ad3 --- /dev/null +++ b/gtestsuite/src/gtest_pthread.cpp @@ -0,0 +1,62 @@ +#include "gtest_pthread.h" + +#if defined(BLIS_DISABLE_SYSTEM) + +#elif defined(_MSC_VER) // !defined(BLIS_DISABLE_SYSTEM) + +#include + +int gtest_pthread_create + ( + bli_pthread_t* thread, + const bli_pthread_attr_t* attr, + void* (*start_routine)(void*), + void* arg + ) +{ + if (attr) return EINVAL; + LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE )start_routine; + thread->handle = CreateThread(NULL, 0, func, arg, 0, NULL); + if ( !thread->handle ) return EAGAIN; + return 0; +} + +int gtest_pthread_join + ( + bli_pthread_t thread, + void** retval + ) +{ + return bli_pthread_join(thread, retval); +} + +#else // !defined(BLIS_DISABLE_SYSTEM) && !defined(_MSC_VER) + +// This branch defines a pthreads-like API, bli_pthreads_*(), and implements it +// in terms of the corresponding pthreads_*() types, macros, and function calls. +// This branch is compiled for Linux and other non-Windows environments where +// we assume that *some* implementation of pthreads is provided (although it +// may lack barriers--see below). + +// -- pthread_create(), pthread_join() -- + +int gtest_pthread_create + ( + bli_pthread_t* thread, + const bli_pthread_attr_t* attr, + void* (*start_routine)(void*), + void* arg + ) +{ + return bli_pthread_create( thread, attr, start_routine, arg ); +} + +int gtest_pthread_join + ( + bli_pthread_t thread, + void** retval + ) +{ + return bli_pthread_join( thread , retval ); +} +#endif // !defined(BLIS_DISABLE_SYSTEM) && !defined(_MSC_VER) diff --git a/gtestsuite/src/gtest_pthread.h b/gtestsuite/src/gtest_pthread.h new file mode 100644 index 000000000..0ebb8507f --- /dev/null +++ b/gtestsuite/src/gtest_pthread.h @@ -0,0 +1,29 @@ +#ifndef GTEST_PTHREAD_H +#define GTEST_PTHREAD_H + +#include "blis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int gtest_pthread_create + ( + bli_pthread_t* thread, + const bli_pthread_attr_t* attr, + void* (*start_routine)(void*), + void* arg + ); + + +int gtest_pthread_join + ( + bli_pthread_t thread, + void** retval + ); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/gtestsuite/src/gtest_suite.cpp b/gtestsuite/src/gtest_suite.cpp new file mode 100644 index 000000000..79d262eff --- /dev/null +++ b/gtestsuite/src/gtest_suite.cpp @@ -0,0 +1,2379 @@ +#include "blis_test.h" +#include "gtest_pthread.h" + +extern vector inputData; + +using namespace std; + +/*****************************Utility Operations******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_RANDV ) +{ + unsigned int id = 0; + const char* op_str = "randv"; + const char* o_types = "v"; // x + const char* p_types = ""; // (no parameters) + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->randv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_randv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for randv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_RANDM ) +{ + unsigned int id = 0; + const char* op_str = "randm"; + const char* o_types = "m"; // a + const char* p_types = ""; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->randm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_randm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for randm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} +/********************* End Of Utility Operations *****************************/ + +/*****************************Level-1V Operations******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_ADDV ) +{ + unsigned int id = 0; + const char* op_str = "addv"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->addv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_addv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for addv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_AMAXV ) +{ + unsigned int id = 0; + const char* op_str = "amaxv"; + const char* o_types = "v"; // x + const char* p_types = ""; // (no parameters) + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->amaxv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_amaxv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for amaxv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_AXPBYV ) +{ + unsigned int id = 0; + const char* op_str = "axpbyv"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->axpbyv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_axpbyv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for axpbyv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_AXPYV ) +{ + unsigned int id = 0; + const char* op_str = "axpyv"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->axpyv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_axpyv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for axpyv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_COPYV ) +{ + unsigned int id = 0; + const char* op_str = "copyv"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->copyv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_copyv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for copyv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_DOTV ) +{ + unsigned int id = 0; + const char* op_str = "dotv"; + const char* o_types = "vv"; // x y + const char* p_types = "cc"; // conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->dotv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_dotv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for dotv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_DOTXV ) +{ + unsigned int id = 0; + const char* op_str = "dotxv"; + const char* o_types = "vv"; // x y + const char* p_types = "cc"; // conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->dotxv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_dotxv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for dotxv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_NORMFV ) +{ + unsigned int id = 0; + const char* op_str = "normfv"; + const char* o_types = "v"; // x + const char* p_types = ""; // (no parameters) + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->normfv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_normfv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for normfv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SCAL2V ) +{ + unsigned int id = 0; + const char* op_str = "scal2v"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->scal2v); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_scal2v_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for scal2v : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SCALV ) +{ + unsigned int id = 0; + const char* op_str = "scalv"; + const char* o_types = "v"; // x + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->scalv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_scalv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for scalv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SETV ) +{ + unsigned int id = 0; + const char* op_str = "setv"; + const char* o_types = "v"; // x + const char* p_types = ""; // (no parameters) + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->setv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_setv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for setv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SUBV ) +{ + unsigned int id = 0; + const char* op_str = "subv"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->subv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_subv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for subv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + /********************* End Of Level-1V Operations *****************************/ + + /*****************************Level-1F Operations******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_XPBYV ) +{ + unsigned int id = 0; + const char* op_str = "xpbyv"; + const char* o_types = "vv"; // x y + const char* p_types = "c"; // conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->xpbyv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_xpbyv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for xpbyv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_AXPY2V ) +{ + unsigned int id = 0; + const char* op_str = "axpy2v"; + const char* o_types = "vvv"; // x y z + const char* p_types = "cc"; // conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->axpy2v); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_axpy2v_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for axpy2v : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_DOTAXPYV ) +{ + unsigned int id = 0; + const char* op_str = "dotaxpyv"; + const char* o_types = "vvv"; // x y z + const char* p_types = "ccc"; // conjxt conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->dotaxpyv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_dotaxpyv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for dotaxpyv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_AXPYF ) +{ + unsigned int id = 0; + const char* op_str = "axpyf"; + const char* o_types = "mvv"; // A x y + const char* p_types = "cc"; // conja conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->axpyf); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_axpyf_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for axpyf : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_DOTXF ) +{ + unsigned int id = 0; + const char* op_str = "dotxf"; + const char* o_types = "mvv"; // A x y + const char* p_types = "cc"; // conjat conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->dotxf); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_dotxf_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for dotxf : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_DOTXAXPYF ) +{ + unsigned int id = 0; + const char* op_str = "dotxaxpyf"; + const char* o_types = "mvvvv"; // A w x y z + const char* p_types = "cccc"; // conjat conja conjw conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->dotxaxpyf); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_dotxaxpyf_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for dotxaxpyf : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + /********************* End Of Level-1F Operations *****************************/ + + /*****************************Level-1M Operations******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_ADDM ) +{ + unsigned int id = 0; + const char* op_str = "addm"; + const char* o_types = "mm"; // x y + const char* p_types = "h"; // transx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->addm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_addm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for addm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_AXPYM ) +{ + unsigned int id = 0; + const char* op_str = "axpym"; + const char* o_types = "mm"; // x y + const char* p_types = "h"; // transx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->axpym); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_axpym_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for axpym : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_COPYM ) +{ + unsigned int id = 0; + const char* op_str = "copym"; + const char* o_types = "mm"; // x y + const char* p_types = "h"; // transx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->copym); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_copym_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for copym : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_NORMFM ) +{ + unsigned int id = 0; + const char* op_str = "normfm"; + const char* o_types = "m"; // x + const char* p_types = ""; // (no parameters) + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->normfm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_normfm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for normfm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SCAL2M ) +{ + unsigned int id = 0; + const char* op_str = "scal2m"; + const char* o_types = "mm"; // x y + const char* p_types = "h"; // transx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->scal2m); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_scal2m_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for scal2m : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SCALM ) +{ + unsigned int id = 0; + const char* op_str = "scalm"; + const char* o_types = "m"; // x + const char* p_types = "c"; // conjbeta + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->scalm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_scalm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for scalm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SETM ) +{ + unsigned int id = 0; + const char* op_str = "setm"; + const char* o_types = "m"; // x + const char* p_types = ""; // (no parameters) + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->setm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_setm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for setm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SUBM ) +{ + unsigned int id = 0; + const char* op_str = "subm"; + const char* o_types = "mm"; // x y + const char* p_types = "h"; // transx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->subm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_subm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for subm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_XPBYM ) +{ + unsigned int id = 0; + const char* op_str = "xpbym"; + const char* o_types = "mm"; // x y + const char* p_types = "h"; // transx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->xpbym); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_xpbym_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for xpbym : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); + } + /********************* End Of Level-1M Operations *****************************/ + /* */ + /*****************************Level-2 Operations*******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_GEMV ) +{ + unsigned int id = 0; + const char* op_str = "gemv"; + const char* o_types = "mvv"; // a x y + const char* p_types = "hc"; // transa conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_GER ) +{ + unsigned int id; + const char* op_str = "ger"; + const char* o_types = "vvm"; // x y a + const char* p_types = "cc"; // conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->ger); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_ger_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for ger : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_HEMV ) +{ + unsigned int id; + const char* op_str = "hemv"; + const char* o_types = "mvv"; // a x y + const char* p_types = "ucc"; // uploa conja conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->hemv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_hemv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for hemv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_L2HER ) +{ + unsigned int id; + const char* op_str = "her"; + const char* o_types = "vm"; // x a + const char* p_types = "uc"; // uploa conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->her); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_her_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for her : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_L2HER2 ) +{ + unsigned int id; + const char* op_str = "her2"; + const char* o_types = "vvm"; // x y a + const char* p_types = "ucc"; // uploa conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->her2); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_her2_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for her2 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SYMV ) +{ + unsigned int id; + const char* op_str = "symv"; + const char* o_types = "mvv"; // a x y + const char* p_types = "ucc"; // uploa conja conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->symv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_symv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for symv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_L2SYR ) +{ + unsigned int id; + const char* op_str = "syr"; + const char* o_types = "vm"; // x a + const char* p_types = "uc"; // uploa conjx + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->syr); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_syr_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for syr : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_L2SYR2 ) +{ + unsigned int id; + const char* op_str = "syr2"; + const char* o_types = "vvm"; // x y a + const char* p_types = "ucc"; // uploa conjx conjy + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->syr2); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_syr2_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for syr2 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_TRMV ) +{ + unsigned int id; + const char* op_str = "trmv"; + const char* o_types = "mv"; // a x + const char* p_types = "uhd"; // uploa transa diaga + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->trmv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_trmv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for trmv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_TRSV ) +{ + unsigned int id; + const char* op_str = "trsv"; + const char* o_types = "mv"; // a x + const char* p_types = "uhd"; // uploa transa diaga + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->trsv); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_trsv_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for trsv : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} +/********************* End Of Level-2 Operations *****************************/ +/* */ +/*****************************Level-3 Operations******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_GEMM ) +{ + unsigned int id; + const char* op_str = "gemm"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_L3GEMMT ) +{ + unsigned int id; + const char* op_str = "gemmt"; + const char* o_types = "mmm"; // a b c + const char* p_types = "uhh"; // uploc transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemmt); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemmt_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemmt: %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_HEMM ) +{ + unsigned int id; + const char* op_str = "hemm"; + const char* o_types = "mmm"; // a b c + const char* p_types = "su"; // side uploa + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->hemm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_hemm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for hemm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_HERK ) +{ + unsigned int id; + const char* op_str = "herk"; + const char* o_types = "mm"; // a c + const char* p_types = "uc"; // uploc trans + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->herk); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_herk_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for herk : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_HER2K ) +{ + unsigned int id; + const char* op_str = "her2k"; + const char* o_types = "mmm"; // a b c + const char* p_types = "uc"; // uploc trans + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->her2k); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_her2k_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for her2k : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SYMM ) +{ + unsigned int id; + const char* op_str = "symm"; + const char* o_types = "mmm"; // a b c + const char* p_types = "su"; // side uploa + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->symm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_symm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for symm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SYRK ) +{ + unsigned int id; + const char* op_str = "syrk"; + const char* o_types = "mm"; // a c + const char* p_types = "uh"; // uploc trans + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->syrk); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_syrk_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for syrk : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_SYR2K ) +{ + unsigned int id; + const char* op_str = "syr2k"; + const char* o_types = "mmm"; // a b c + const char* p_types = "uh"; // uploc trans + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->syr2k); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_syr2k_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for syr2k : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_L3TRMM ) +{ + unsigned int id; + const char* op_str = "trmm"; + const char* o_types = "mm"; // a b + const char* p_types = "suhd"; // side uploa transa diaga + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->trmm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_trmm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for trmm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_TRMM3 ) +{ + unsigned int id; + const char* op_str = "trmm3"; + const char* o_types = "mmm"; // a b c + const char* p_types = "suhdh"; // side uploa transa diaga transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->trmm3); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_trmm3_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for trmm3 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_BLIS_TRSM ) +{ + unsigned int id; + const char* op_str = "trsm"; + const char* o_types = "mm"; // a b + const char* p_types = "suhd"; // side uploa transa diaga + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->trsm); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_trsm_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for trsm : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} +/********************* End Of Level-3 Operations *****************************/ +/* */ +/***************************** LPGEMM Operations *****************************/ +TEST_P( AoclBlisTestFixture, AOCL_GEMM_S32S32 ) +{ + unsigned int id; + const char* op_str = "gemm_u8s8s32os32"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_u8s8s32os32); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_u8s8s32os32_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_u8s8s32os32 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_GEMM_S8S32 ) +{ + unsigned int id; + const char* op_str = "gemm_u8s8s32os8"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_u8s8s32os8); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_u8s8s32os8_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_u8s8s32os8 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_GEMM_F32F32 ) +{ + unsigned int id; + const char* op_str = "gemm_f32f32f32of32"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_f32f32f32of32); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_f32f32f32of32_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_f32f32f32of32 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_GEMM_S8S16 ) +{ + unsigned int id; + const char* op_str = "gemm_u8s8s16os8"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_u8s8s16os8); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_u8s8s16os8_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_u8s8s16os8 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_GEMM_S16S16 ) +{ + unsigned int id; + const char* op_str = "gemm_u8s8s16os16"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_u8s8s16os16); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_u8s8s16os16_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_u8s8s16os16 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_GEMM_BF16BF16 ) +{ + unsigned int id; + const char* op_str = "gemm_bf16bf16f32obf16"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_bf16bf16f32obf16); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_bf16bf16f32obf16_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_bf16bf16f32obf16 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} + +TEST_P( AoclBlisTestFixture, AOCL_GEMM_F32BF16 ) +{ + unsigned int id; + const char* op_str = "gemm_bf16bf16f32of32"; + const char* o_types = "mmm"; // a b c + const char* p_types = "hh"; // transa transb + iface_t iface = BLIS_TEST_SEQ_FRONT_END; + test_op_t* op = &(ops->gemm_bf16bf16f32of32); + + libblis_test_preprocess_params( params, op, iface, p_types, o_types ); + + for( id = 0 ; id < nt ; id++ ) + { + tdata[id].params = params; + tdata[id].op = op; + tdata[id].str = op_str; + tdata[id].nt = nt; + tdata[id].id = id; + tdata[id].iface = iface; + tdata[id].xc = 0; + tdata[id].barrier = barrier; + tdata[id].pfr = pfr; + gtest_pthread_create( &pthread[id], NULL, + libblis_test_gemm_bf16bf16f32of32_thread_entry, + (void *)&tdata[id] ); + } + + // Thread 0 waits for additional threads to finish. + for( id = 0 ; id < nt ; id++ ) + { + gtest_pthread_join( pthread[id], NULL ); + } + + printf( "\n" ); + printf( "Total test cases for gemm_bf16bf16f32of32 : %d\n", pfr->tcnt ); + printf( "Total test cases passed : %d\n", (pfr->tcnt - pfr->cntf) ); + printf( "Total test cases failed : %d\n", pfr->cntf ); + + AoclBlisTestFixture::destroy_params( params ); +} +/********************* End Of Level-3 Operations *****************************/ +/* */ +/********************* FILE_READ from InputFile ******************************/ +TEST_P( AoclBlisTestFixture, AOCL_BLIS_READ_INPUTFILE ) +{ + AoclBlisTestFixture::create_params( params ); + + libblis_test_read_params_inpfile( pfile->inputfile, params, ops, pfr ); + + AoclBlisTestFixture::destroy_params( params ); +} +/******************************************************************************************/ +/* END OF OPERATIONS */ +/******************************************************************************************/ + + +/******************************************************************************************/ +/*** INSTANTIATE_TEST_SUITE_P ***/ +/******************************************************************************************/ +INSTANTIATE_TEST_SUITE_P( AoclBlisTests, + AoclBlisTestFixture, + ::testing::ValuesIn( inputData ) ); +/******************************************************************************************/ diff --git a/gtestsuite/src/lpgemm_utils.cpp b/gtestsuite/src/lpgemm_utils.cpp new file mode 100644 index 000000000..49af172cb --- /dev/null +++ b/gtestsuite/src/lpgemm_utils.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS + +bfloat16 mat_mul_accuracy_check_downscale_bf16( float temp_accum, bfloat16 out_temp_accum, + aocl_post_op* post_op, dim_t j) +{ + float_to_bf16( ( &temp_accum ), ( &out_temp_accum ) ); + return out_temp_accum; +} + +float bf16_to_float( bfloat16 bf16_val ) +{ + int32_t inter_temp = *( ( int16_t* ) &bf16_val ); + inter_temp = inter_temp << 16; + float float_value = *( float* ) ( &inter_temp ); + return float_value; +} + +float mat_mul_accuracy_check_accum_bf16 + ( + bfloat16* a, + bfloat16* b, + float* c_ref, + float temp_accum, + float alpha, + float beta, + dim_t rs_a, + dim_t rs_b, + dim_t cs_a, + dim_t cs_b, + dim_t rs_c_ref, + dim_t cs_c_ref, + dim_t i, + dim_t j, + dim_t k + ) +{ + for ( dim_t p = 0; p < k; ++p) + { + float a_float = bf16_to_float( *( a + i * rs_a + p * cs_a ) ); + float b_float = bf16_to_float( *( b + p * rs_b + j * cs_b ) ); + temp_accum += ( ( a_float ) * ( b_float ) ); + } + temp_accum = ( beta * ( * (c_ref + ( rs_c_ref * i ) + ( cs_c_ref * j ) ) ) ) + + ( alpha * temp_accum ); + return temp_accum; +} + +float mat_mul_accuracy_check_accum_bf16 + ( + bfloat16* a, + bfloat16* b, + bfloat16* c_ref, + float temp_accum, + float alpha, + float beta, + dim_t rs_a, + dim_t rs_b, + dim_t cs_a, + dim_t cs_b, + dim_t rs_c_ref, + dim_t cs_c_ref, + dim_t i, + dim_t j, + dim_t k + ) +{ + for ( dim_t p = 0; p < k; ++p) + { + float a_float = bf16_to_float( *( a + i*rs_a + p*cs_a ) ); + float b_float = bf16_to_float( *( b + p*rs_b + j*cs_b ) ); + temp_accum += ( ( a_float ) * ( b_float ) ); + } + float c_ref_float = bf16_to_float( *( c_ref + i*rs_c_ref + j*cs_c_ref ) ); + temp_accum = ( beta * ( c_ref_float ) ) + ( alpha * temp_accum ); + + return temp_accum; +} + +void lpgemm_destroy_post_ops_struct( aocl_post_op* post_ops ) +{ + if ( post_ops == NULL ) + { + return; + } + + if ( post_ops->eltwise.algo.alpha != NULL ) + { + free( post_ops->eltwise.algo.alpha ); + } + if ( post_ops->sum.scale_factor != NULL ) + { + free( post_ops->sum.scale_factor ); + } + if ( post_ops->bias.bias != NULL ) + { + free( post_ops->bias.bias ); + } + if( post_ops->seq_vector != NULL ) + { + free( post_ops->seq_vector ); + } + + free( post_ops ); +} +#endif \ No newline at end of file diff --git a/gtestsuite/src/lpgemm_utils.h b/gtestsuite/src/lpgemm_utils.h new file mode 100644 index 000000000..c90f421db --- /dev/null +++ b/gtestsuite/src/lpgemm_utils.h @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include + +#include "blis_test.h" + +#ifdef BLIS_ENABLE_ADDONS + +#define S8_MIN (-128) +#define S8_MAX (+127) + +static inline int max (int a, int b) +{ + return ( a > b ? a : b ); +} + +static inline int min (int a, int b) +{ + return ( a < b ? a : b ); +} + +template +void fill_array ( T* arr, dim_t size ) +{ + T* temp_arr = ( T* ) arr; + for ( dim_t i = 0; i < size; ++i ) + { + temp_arr[i] = ( T )( i % 10 ); + } +} + +template +void fill_array_post_ops( T* arr, dim_t size ) +{ + T* temp_arr = ( T* ) arr; + for ( dim_t i = 0; i < size; ++i ) + { + temp_arr[i] = ( T )( i % 20 ); + } +} + +static void float_to_bf16( float* float_value, bfloat16* bf16_val ) +{ + /*Set offset 2 to copy most significant 2 bytes of float + to convert float values to bf16 values*/ + memcpy( ( bf16_val ), (char *)( float_value ) + 2, sizeof ( bfloat16 ) ); +} + +static inline void convert_float_arr_to_bf16( float* array, bfloat16* array_bf16, int size ) +{ + for (int i = 0 ; i < size ; i++) + { + float_to_bf16( ( array + i ), ( array_bf16 + i ) ); + } +} + +/* Only supports bias followed by RELU and vice versa for now.*/ +template +aocl_post_op* lpgemm_create_post_ops_struct( dim_t m, dim_t n, + char* post_ops_str, bool dscale_out ) +{ + aocl_post_op* post_ops = NULL; + post_ops = ( aocl_post_op* ) malloc( sizeof( aocl_post_op ) ); + + if ( ( post_ops == NULL ) && ( dscale_out ) ) + { + return NULL; + } + + /* Only supporting 3 post ops at max for now.*/ + dim_t max_post_ops_seq_length = 3; + post_ops->seq_vector = ( AOCL_POST_OP_TYPE* ) + malloc( max_post_ops_seq_length * sizeof( AOCL_POST_OP_TYPE ) ); + + if ( post_ops->seq_vector == NULL ) + { + free( post_ops ); + return NULL; + } + + /* Parse post ops list.*/ + dim_t cur_op_index = 0; + /* Ensure the buffers that use NULL check in deinit code is properly set to NULL.*/ + post_ops->eltwise.algo.alpha = NULL; + post_ops->bias.bias = NULL; + post_ops->sum.scale_factor = NULL; + if ( post_ops_str != NULL ) + { + char* ops_tok = strtok(post_ops_str, ", " ); + bool is_param_relu = FALSE; + while ( ops_tok ) + { + if ( strcmp( ops_tok, "bias") == 0 ) + { + post_ops->seq_vector[cur_op_index] = BIAS; + } + else if ( strcmp( ops_tok, "relu") == 0 ) + { + post_ops->seq_vector[cur_op_index] = ELTWISE; + } + else if ( strcmp( ops_tok, "prelu") == 0 ) + { + post_ops->seq_vector[cur_op_index] = ELTWISE; + is_param_relu = TRUE; + } + ops_tok = strtok( NULL, ", " ); + cur_op_index++; + } + + /* Allocate bias buffer, return early if alloc fails.*/ + post_ops->bias.bias = malloc( n * sizeof( X ) ); + if ( post_ops->bias.bias == NULL ) + { + free( post_ops->seq_vector ); + free( post_ops ); + return NULL; + } + fill_array_post_ops((X*)post_ops->bias.bias, n ); + + post_ops->eltwise.is_power_of_2 = FALSE; + post_ops->eltwise.scale_factor = NULL; + post_ops->eltwise.algo.alpha = NULL; + post_ops->eltwise.algo.algo_type = RELU; + if ( is_param_relu == TRUE ) + { + post_ops->eltwise.algo.alpha = malloc( sizeof( X ) ); + *( ( X* ) post_ops->eltwise.algo.alpha ) = ( X )6; + post_ops->eltwise.algo.algo_type = PRELU; + } + post_ops->eltwise.algo.beta = NULL; + } + + if ( dscale_out ) + { + post_ops->seq_vector[cur_op_index] = SCALE; + cur_op_index++; + + post_ops->sum.is_power_of_2 = FALSE; + post_ops->sum.scale_factor = NULL; + post_ops->sum.buff = NULL; + post_ops->sum.zero_point = NULL; + if ( dscale_out ) + { + /* Allocate scale buffer, return early if alloc fails.*/ + post_ops->sum.scale_factor = malloc( n * sizeof( Z ) ); + if ( post_ops->sum.scale_factor == NULL ) + { + free ( post_ops->bias.bias ); + free( post_ops->seq_vector ); + free( post_ops ); + return NULL; + } + /* Fill scale factor.*/ + Z* temp_dscale_ptr = ( Z* )post_ops->sum.scale_factor; + for ( dim_t i = 0; i < n; ++i ) + { + temp_dscale_ptr[i] = ( ( Z )1 )/ ( ( Z )1000 ); + } + } + } + + post_ops->seq_length = cur_op_index; + + return post_ops; +} + +void lpgemm_destroy_post_ops_struct( aocl_post_op* post_ops ); + +template +B mat_mul_accuracy_check_downscale( Z temp_accum, B out_temp_accum, + aocl_post_op* post_op, dim_t j ) +{ + out_temp_accum = ( B ) min ( max ( nearbyintf( ( ST )temp_accum * + ( *( ( ST* )post_op->sum.scale_factor + j ) ) ), S8_MIN ), S8_MAX ) ; + return out_temp_accum; +} + +template +Z mat_mul_accuracy_check_accum + ( + A* a, + B* b, + X* c_ref, + Z temp_accum, + Z alpha, + Z beta, + dim_t rs_a, + dim_t rs_b, + dim_t cs_a, + dim_t cs_b, + dim_t rs_c_ref, + dim_t cs_c_ref, + dim_t i, + dim_t j, + dim_t k + ) +{ + dim_t p; + + for( p = 0 ; p < k ; ++p ) + { + temp_accum += ( *( a + ( i * rs_a ) + ( cs_a * p ) ) * + *( b + ( rs_b * p ) + ( cs_b * j ) ) ); + } + + temp_accum = ( beta * ( * (c_ref + ( rs_c_ref * i ) + ( cs_c_ref * j ) ) ) ) + + ( alpha * temp_accum ); + return temp_accum; +} + +template +double mat_mul_accuracy_check_driver + ( + dim_t m, + dim_t n, + dim_t k, + Z alpha, + A* a, + dim_t rs_a, + dim_t cs_a, + B* b, + dim_t rs_b, + dim_t cs_b, + Z beta, + X* c, + dim_t rs_c, + dim_t cs_c, + X* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_c_ref = rs_c; + dim_t cs_c_ref = cs_c; + dim_t i,j; + + for( i = 0 ; i < m ; ++i ) + { + for( j = 0 ; j < n ; ++j ) + { + Z temp_accum = 0; + X out_temp_accum = 0; + + temp_accum = mat_mul_accuracy_check_accum (a, b, c_ref, + temp_accum, alpha, beta, rs_a, rs_b, cs_a, cs_b, + rs_c_ref, cs_c_ref, i, j, k); + + if ( post_op != NULL ) + { + /* Apply bias followed by relu. */ + if ( post_op->seq_vector[0] == BIAS ) + { + if ( post_op->seq_length >= 1 ) + { + temp_accum += ( *( ( Z* )post_op->bias.bias + j ) ); + } + if ( ( post_op->seq_length > 1 ) && + ( post_op->seq_vector[1] == ELTWISE ) ) + { + if ( post_op->eltwise.algo.alpha != NULL ) /* PReLU*/ + { + temp_accum = ( temp_accum > 0 ) ? + temp_accum : + ( temp_accum * + *( ( Z* ) post_op->eltwise.algo.alpha ) ); + } + else + { + temp_accum = ( temp_accum > 0 ) ? temp_accum : 0 ; + } + } + } + else if ( post_op->seq_vector[0] == ELTWISE ) + { + if ( post_op->seq_length >= 1 ) + { + if ( post_op->eltwise.algo.alpha != NULL ) /* PReLU*/ + { + temp_accum = ( temp_accum > 0 ) ? + temp_accum : + ( temp_accum * *( ( Z* ) post_op->eltwise.algo.alpha ) ); + } + else + { + temp_accum = ( temp_accum > 0 ) ? temp_accum : 0 ; + } + } + if ( ( post_op->seq_length > 1 ) && ( post_op->seq_vector[1] == BIAS ) ) + { + temp_accum += ( *( ( Z* )post_op->bias.bias + j ) ); + } + } + } + if ( dscale_out ) + { + out_temp_accum = mat_mul_accuracy_check_downscale + ( temp_accum, out_temp_accum, post_op, j); + } + else + { + out_temp_accum = ( X )temp_accum; + } + + if( *( c + ( rs_c * i ) + ( cs_c * j ) ) != out_temp_accum ) + { + auto tmp = *( c + ( rs_c * i ) + ( cs_c * j ) ); + resid += abs( tmp - out_temp_accum ); + //return resid; + } + } + } + return resid; +} + +bfloat16 mat_mul_accuracy_check_downscale_bf16 + ( float temp_accum, bfloat16 out_temp_accum, aocl_post_op* post_op, dim_t j); + +float bf16_to_float( bfloat16 bf16_val ); + +float mat_mul_accuracy_check_accum_bf16 + ( + bfloat16* a, + bfloat16* b, + float* c_ref, + float temp_accum, + float alpha, + float beta, + dim_t rs_a, + dim_t rs_b, + dim_t cs_a, + dim_t cs_b, + dim_t rs_c_ref, + dim_t cs_c_ref, + dim_t i, + dim_t j, + dim_t k + ); + +float mat_mul_accuracy_check_accum_bf16 + ( + bfloat16* a, + bfloat16* b, + bfloat16* c_ref, + float temp_accum, + float alpha, + float beta, + dim_t rs_a, + dim_t rs_b, + dim_t cs_a, + dim_t cs_b, + dim_t rs_c_ref, + dim_t cs_c_ref, + dim_t i, + dim_t j, + dim_t k + ); + +template +double mat_mul_accuracy_check_driver_bf16 + ( + dim_t m, + dim_t n, + dim_t k, + Z alpha, + A* a, + dim_t rs_a, + dim_t cs_a, + B* b, + dim_t rs_b, + dim_t cs_b, + Z beta, + X* c, + dim_t rs_c, + dim_t cs_c, + X* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_c_ref = rs_c; + dim_t cs_c_ref = cs_c; + + for ( dim_t i = 0; i < m; ++i ) + { + for ( dim_t j = 0; j < n; ++j ) + { + Z temp_accum = 0; + X out_temp_accum = 0; + + temp_accum = mat_mul_accuracy_check_accum_bf16(a, b, c_ref, + temp_accum, alpha, beta, rs_a, rs_b, cs_a, cs_b, + rs_c_ref, cs_c_ref, i, j, k); + + if ( post_op != NULL ) + { + /* Apply bias followed by relu. */ + if ( post_op->seq_vector[0] == BIAS ) + { + if ( post_op->seq_length >= 1 ) + { + temp_accum += ( *( ( Z* )post_op->bias.bias + j ) ); + } + if ( ( post_op->seq_length > 1 ) && + ( post_op->seq_vector[1] == ELTWISE ) ) + { + if ( post_op->eltwise.algo.alpha != NULL ) /* PReLU*/ + { + temp_accum = ( temp_accum > 0 ) ? + temp_accum : + ( temp_accum * + *( ( Z* ) post_op->eltwise.algo.alpha ) ); + } + else + { + temp_accum = ( temp_accum > 0 ) ? temp_accum : 0 ; + } + } + } + else if ( post_op->seq_vector[0] == ELTWISE ) + { + if ( post_op->seq_length >= 1 ) + { + if ( post_op->eltwise.algo.alpha != NULL ) /* PReLU*/ + { + temp_accum = ( temp_accum > 0 ) ? + temp_accum : + ( temp_accum * *( ( Z* ) post_op->eltwise.algo.alpha ) ); + } + else + { + temp_accum = ( temp_accum > 0 ) ? temp_accum : 0 ; + } + } + if ( ( post_op->seq_length > 1 ) && ( post_op->seq_vector[1] == BIAS ) ) + { + temp_accum += ( *( ( Z* )post_op->bias.bias + j ) ); + } + } + } + if ( dscale_out ) + { + out_temp_accum = mat_mul_accuracy_check_downscale_bf16 + ( temp_accum, out_temp_accum, post_op, j); + } + else + { + out_temp_accum = ( X )temp_accum; + } + + if( *( c + ( rs_c * i ) + ( cs_c * j ) ) != out_temp_accum ) + { + auto tmp = *( c + ( rs_c * i ) + ( cs_c * j ) ); + resid += abs( tmp - out_temp_accum ); + //return resid; + } + } + } + return resid; +} +#endif + +template +void print_matrics(T *x , dim_t mm, dim_t nn, dim_t ld) +{ + dim_t i,j; + int32_t val; + for ( i = 0; i < mm; ++i ) { + for ( j = 0; j < nn; ++j ) { + val = (int32_t)(x[i*ld + j]); + printf("%9d", val); + } + cout << endl; + } + cout << endl; + return; +} + diff --git a/gtestsuite/src/main.cpp b/gtestsuite/src/main.cpp new file mode 100644 index 000000000..8430db35d --- /dev/null +++ b/gtestsuite/src/main.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include +#include +#include +#include + +#include "blis_test.h" + +vector inputData; + +int main(int argc, char **argv) +{ + BlisTestSuite bts; + test_params_t* params = NULL; + blis_string_t* strData = NULL; + test_ops_t* ops = NULL; + input_file_t* pfile = NULL; + input_data_t inData; + string filter_data( "" ); + + params = bts.getParamsStr(); + strData = bts.getStgStr(); + ops = bts.getOpsStr(); + pfile = bts.getfileStr(); + + unsigned int n = std::thread::hardware_concurrency(); + std::cout << n << " concurrent threads are supported.\n"; + + memset( &inData, 0, sizeof( input_data_t ) ); + memset( params, 0, sizeof( test_params_t ) ); + memset( strData, 0, sizeof( blis_string_t ) ); + memset( ops, 0, sizeof( test_ops_t ) ); + memset( pfile, 0, sizeof( input_file_t ) ); + + // Initialize some strings. + bts.libblis_test_init_strings( strData ); + + if(argc <= 1) + { + // Read the global parameters file. + bts.libblis_test_read_params_file( strData->libblis_test_parameters_filename, + params, strData->libblis_test_alphabeta_parameter); + + // Read the operations parameter file. + bts.libblis_test_read_ops_file(strData->libblis_test_operations_filename, ops); + + bts.CreateGtestFilters(ops, filter_data); + } + else + { + // Read the global parameters file. + bts.libblis_test_inpfile(argv[1], pfile); + + bts.CreateGtestFilters_api(pfile, filter_data); + } + + inData.params = params; + inData.ops = ops; + inData.pfile = pfile; + if(pfile->fileread != 1) { + inData.pthread = (bli_pthread_t *)malloc( sizeof( bli_pthread_t ) * params->n_app_threads ); + inData.tdata = (thread_data_t *)malloc( sizeof( thread_data_t ) * params->n_app_threads ); + } + + inputData.push_back(inData); + + ::testing::GTEST_FLAG(filter) = filter_data.c_str(); + testing::InitGoogleTest(&argc, argv); + int retval = RUN_ALL_TESTS(); + + if(pfile->fileread != 1) { + free( inData.pthread ); + free( inData.tdata ); + } + + return retval; +} diff --git a/gtestsuite/src/ref_addm.cpp b/gtestsuite/src/ref_addm.cpp new file mode 100644 index 000000000..60ccb2148 --- /dev/null +++ b/gtestsuite/src/ref_addm.cpp @@ -0,0 +1,130 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_addm.h" + +using namespace std; + +//* ========================================================================== +//*> ADDM performs matrix operations +//*> B := B + transa(A) +//*> where B is an m x n matrix. +//* ========================================================================== + +template +void libblis_iaddm_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx, + T* Y, dim_t rsy, dim_t csy, T* YY) { + + dim_t i, j; + + if ((M == 0) || (N == 0)) { + return; + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = Y[i*rsy + j*csy] + X[i*rsx + j*csx]; + } + } + + return; +} + +template +void libblis_icaddm_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx, + conj_t conjx, T* Y, dim_t rsy, dim_t csy) { + + dim_t i, j; + + if ((M == 0) || (N == 0)) { + return; + } + + if(conjx) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = conjugate(X[i*rsx + j*csx]); + } + } + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = addc(Y[i*rsy + j*csy], X[i*rsx + j*csx]); + } + } + + return; +} + +double libblis_test_iaddm_check( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + bool transx = bli_obj_has_trans( x ); + conj_t conjx = bli_obj_conj_status( x ); + dim_t M = bli_obj_length( y ); + dim_t N = bli_obj_width( y ); + dim_t rsy = bli_obj_row_stride( y ) ; + dim_t csy = bli_obj_col_stride( y ) ; + double resid = 0.0; + dim_t rsx, csx; + + if( bli_obj_is_col_stored( x ) ) { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } else { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iaddm_check( M, N, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iaddm_check( M, N, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icaddm_check( M, N, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icaddm_check( M, N, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_addv.cpp b/gtestsuite/src/ref_addv.cpp new file mode 100644 index 000000000..b48044798 --- /dev/null +++ b/gtestsuite/src/ref_addv.cpp @@ -0,0 +1,117 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_addv.h" + +using namespace std; + +//* ========================================================================== +//*> ADDV performs vector operations +//*> y := y + conjx(x) +//*> where x and y are vectors of length n. +//* ========================================================================== + +template +void libblis_iaddv_check(dim_t len, T* X, dim_t incx, T* Y, dim_t incy) { + + dim_t i, ix, iy; + if (len == 0) { + return; + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = Y[iy] + X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +template +void libblis_icaddv_check(dim_t len, T* X, dim_t incx, T* Y, + dim_t incy, bool cfx) { + dim_t i, ix, iy; + if (len == 0) { + return; + } + + ix = 0; + if(cfx) { + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = addc(Y[iy] , X[ix]); + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +double libblis_test_iaddv_check( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t incy = bli_obj_vector_inc( y_orig ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iaddv_check( M, X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iaddv_check( M, X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icaddv_check( M, X, incx, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icaddv_check( M, X, incx, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_amaxv.cpp b/gtestsuite/src/ref_amaxv.cpp new file mode 100644 index 000000000..e112a68ec --- /dev/null +++ b/gtestsuite/src/ref_amaxv.cpp @@ -0,0 +1,105 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_amaxv.h" + +using namespace std; + +//* ========================================================================== +//*> Given a vector of length n, return the zero-based index index of +//*> the element of vector x that contains the largest absolute value +//*> (or, in the complex domain, the largest complex modulus). +//* ========================================================================== + +template +dim_t libblis_iamaxv_check(dim_t len, T* X, dim_t incx) { + + dim_t i, ix, iamax = 0; + + if (len == 0) { + return 0; + } + + ix = 0; + T smax = abs(X[ix]); + for(i = 0 ; i < len ; i++) { + if(abs(X[ix]) > smax) { + iamax = i; + smax = abs(X[ix]); + } + ix = ix + incx; + } + + return iamax; +} + +template +dim_t libblis_icamaxv_check(dim_t len, T* X, dim_t incx) { + + dim_t i, ix, iamax = 0; + if (len == 0) { + return 0; + } + + ix = 0; + U smax = abscomplex(X[ix]); + for(i = 0 ; i < len ; i++) { + if(abscomplex(X[ix]) > smax) { + iamax = i; + smax = abscomplex(X[ix]); + } + ix = ix + incx; + } + + return iamax; +} + +double libblis_test_iamaxv_check( + test_params_t* params, + obj_t* x, + obj_t* index +){ + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + double resid = 0.0; + dim_t ind = 0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + f77_int* indx = (f77_int*) bli_obj_buffer( index ); + ind = libblis_iamaxv_check( M, X, incx ); + resid = (double)(*indx - ind); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + f77_int* indx = (f77_int*) bli_obj_buffer( index ); + ind = libblis_iamaxv_check( M, X, incx ); + resid = (double)(*indx - ind); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + f77_int* indx = (f77_int*) bli_obj_buffer( index ); + ind = libblis_icamaxv_check( M, X, incx ); + resid = (double)(*indx - ind); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + f77_int* indx = (f77_int*) bli_obj_buffer( index ); + ind = libblis_icamaxv_check( M, X, incx ); + resid = (double)(*indx - ind); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} diff --git a/gtestsuite/src/ref_axpbyv.cpp b/gtestsuite/src/ref_axpbyv.cpp new file mode 100644 index 000000000..ab8dec811 --- /dev/null +++ b/gtestsuite/src/ref_axpbyv.cpp @@ -0,0 +1,202 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpbyv.h" + +using namespace std; + +//* ========================================================================== +//*> AXPBYV performs vector operations +//*> y := beta * y + alpha * conjx(x) +//*> where x and y are vectors of length n, and alpha and beta are scalars +//* ========================================================================== + +template +void libblis_iaxpbyv_check(dim_t len, T* alpha, T* X, dim_t incx, + T* beta, T* Y, dim_t incy) { + + dim_t i, ix, iy; + T ONE, ZERO; + ONE = 1.0 ; + ZERO = 0.0 ; + T Alpha = alpha[0]; + T Beta = beta[0]; + if (len == 0){ + return; + } + + //* First form y := beta*y. + if (Beta != ONE) { + iy = 0; + if (Beta == ZERO) { + for(i = 0 ; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = Beta*Y[iy]; + iy = iy + incy; + } + } + } + + if (Alpha != ONE) { + ix = 0; + if (Alpha == ZERO) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = Alpha*X[ix]; + ix = ix + incx; + } + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = Y[iy] + X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +template +void libblis_icaxpbyv_check(dim_t len, T* alpha, T* X, dim_t incx, + T* beta, T* Y, dim_t incy, bool cfx) { + dim_t i, ix, iy; + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + T Beta = *beta; + + if (len == 0) { + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + /* First form y := beta*y. */ + iy = 0; + if ((Beta.real == ZERO.real) && (Beta.imag == ZERO.imag)) { + for(i = 0; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = mulc(Beta , Y[iy]); + iy = iy + incy; + } + } + + ix = 0; + if ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) { + for(i = 0; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = mulc(Alpha , X[ix]); + ix = ix + incx; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = addc(Y[iy] , X[ix]); + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +double libblis_test_iaxpbyv_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iaxpbyv_check( M, Alpha, X, incx, + Beta, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iaxpbyv_check( M, Alpha, X, incx, + Beta, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icaxpbyv_check( M, Alpha, X, incx, + Beta, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icaxpbyv_check( M, Alpha, X, incx, + Beta, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} diff --git a/gtestsuite/src/ref_axpy2v.cpp b/gtestsuite/src/ref_axpy2v.cpp new file mode 100644 index 000000000..fbb973f0b --- /dev/null +++ b/gtestsuite/src/ref_axpy2v.cpp @@ -0,0 +1,231 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpy2v.h" + +using namespace std; + +//* ========================================================================== +//*> AXPY2V performs vector operations +//*> z := y + alphax * conjx(x) + alphay * conjy(y) +//*> where x, y, and z are vectors of length m. The kernel is implemented +//*> as a fused pair of calls to axpyv. +//* ========================================================================== + +template +void libblis_iaxpy2v_check(dim_t len, T* alphax, T* alphay, T* X, dim_t incx, + T* Y, dim_t incy, T* Z, dim_t incz) { + dim_t i, ix, iy, iz; + T ONE, ZERO; + ONE = 1.0 ; + ZERO = 0.0 ; + T Alphax = alphax[0]; + T Alphay = alphay[0]; + + if (len == 0){ + return; + } + + if (Alphax != ONE) { + ix = 0; + if (Alphax == ZERO) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = Alphax * X[ix]; + ix = ix + incx; + } + } + } + + if (Alphay != ONE) { + iy = 0; + if (Alphay == ZERO) { + for(i = 0 ; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = Alphay * Y[iy]; + iy = iy + incy; + } + } + } + + ix = 0; + iy = 0; + iz = 0; + for(i = 0 ; i < len ; i++) { + Z[iz] = Z[iz] + X[ix] + Y[iy] ; + ix = ix + incx; + iy = iy + incy; + iz = iz + incz; + } + + return; +} + +template +void libblis_icaxpy2v_check(dim_t len, T* alphax, T* alphay, T* X, dim_t incx, + bool cfx, T* Y, dim_t incy, bool cfy, T* Z, dim_t incz) { + + dim_t i, ix, iy, iz; + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + T Alphax = *alphax; + T Alphay = *alphay; + + if (len == 0) { + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(cfy) { + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = conjugate(Y[iy]); + iy = iy + incy; + } + } + + if ((Alphax.real != ONE.real) && (Alphax.imag != ONE.imag)) { + ix = 0; + if ((Alphax.real == ZERO.real) && (Alphax.imag == ZERO.imag)) { + for(i = 0; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = mulc(Alphax , X[ix]); + ix = ix + incx; + } + } + } + + if ((Alphay.real != ONE.real) && (Alphay.imag != ONE.imag)) { + iy = 0; + if ((Alphay.real == ZERO.real) && (Alphay.imag == ZERO.imag)) { + for(i = 0; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = mulc(Alphay , Y[iy]); + iy = iy + incy; + } + } + } + + ix = 0; + iy = 0; + iz = 0; + for(i = 0 ; i < len ; i++) { + auto xx = X[ix]; + auto yy = Y[iy]; + auto zz = Z[iz]; + zz.real = zz.real + xx.real + yy.real ; + zz.imag = zz.imag + xx.imag + yy.imag ; + Z[iz] = zz; + ix = ix + incx; + iy = iy + incy; + iz = iz + incz; + } + + return; +} + +double libblis_test_iaxpy2v_check ( + test_params_t* params, + obj_t* alphax, + obj_t* alphay, + obj_t* x, + obj_t* y, + obj_t* z, + obj_t* z_orig +) { + num_t dt = bli_obj_dt( z ); + dim_t M = bli_obj_vector_dim( z ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + bool cfx = bli_obj_has_conj( x ); + bool cfy = bli_obj_has_conj( y ); + f77_int incz = bli_obj_vector_inc( z ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alphax = (float*) bli_obj_buffer( alphax ); + float* Alphay = (float*) bli_obj_buffer( alphay ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y ); + float* Z = (float*) bli_obj_buffer( z_orig ); + float* ZZ = (float*) bli_obj_buffer( z ); + libblis_iaxpy2v_check( M, Alphax, Alphay, X, incx, + Y, incy, Z, incz ); + resid = computediffrv(M, incz, ZZ, Z); + break; + } + case BLIS_DOUBLE : + { + double* Alphax = (double*) bli_obj_buffer( alphax ); + double* Alphay = (double*) bli_obj_buffer( alphay ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y ); + double* Z = (double*) bli_obj_buffer( z_orig ); + double* ZZ = (double*) bli_obj_buffer( z ); + libblis_iaxpy2v_check( M, Alphax, Alphay, X, incx, + Y, incy, Z, incz ); + resid = computediffrv(M, incz, ZZ, Z); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alphax = (scomplex*) bli_obj_buffer( alphax ); + scomplex* Alphay = (scomplex*) bli_obj_buffer( alphay ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* Z = (scomplex*) bli_obj_buffer( z_orig ); + scomplex* ZZ = (scomplex*) bli_obj_buffer( z ); + libblis_icaxpy2v_check( M, Alphax, Alphay, X, incx, + cfx, Y, incy, cfy, Z, incz ); + resid = computediffiv(M, incz, ZZ, Z); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alphax = (dcomplex*) bli_obj_buffer( alphax ); + dcomplex* Alphay = (dcomplex*) bli_obj_buffer( alphay ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* Z = (dcomplex*) bli_obj_buffer( z_orig ); + dcomplex* ZZ = (dcomplex*) bli_obj_buffer( z ); + libblis_icaxpy2v_check( M, Alphax, Alphay, X, incx, + cfx, Y, incy, cfy, Z, incz ); + resid = computediffiv(M, incz, ZZ, Z); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_axpyf.cpp b/gtestsuite/src/ref_axpyf.cpp new file mode 100644 index 000000000..021a44b66 --- /dev/null +++ b/gtestsuite/src/ref_axpyf.cpp @@ -0,0 +1,156 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpyv.h" + +using namespace std; + +//* ========================================================================== +//*> AXPYF performs vector operations +//*> y := y + alpha * conja(A) * conjx(x) +//*> where A is an m x b matrix, and y and x are vectors. +//*> The kernel is implemented as a fused series of calls to axpyv +//*> where b is less than or equal to an implementation-dependent +//*> fusing factor specific to axpyf +//* ========================================================================== + +template +void libblis_iaxpyf_check(dim_t M, dim_t N, T* alpha, T* A, dim_t rsa, + dim_t csa, T* X, dim_t incx, T* Y, dim_t incy) { + dim_t i, j, ix, iy; + T Alpha = alpha[0]; + T temp; + if((M == 0) || (N == 0)) { + return; + } + + ix = 0; + for(j = 0 ; j < N ; j++) { + temp = Alpha * X[ix]; + iy = 0; + for(i = 0 ; i < M ; i++) { + Y[iy] = Y[iy] + temp * A[i*rsa + j*csa]; + iy = iy + incy; + } + ix = ix + incx; + } + + return; +} + +template +void libblis_icaxpyf_check(dim_t M, dim_t N, T* alpha, T* A, dim_t rsa, + dim_t csa, bool cfa, T* X, dim_t incx, bool cfx, T* Y, dim_t incy ) { + + dim_t i, j, ix, iy; + T Alpha = *alpha; + T temp; + + if((M == 0) || (N == 0)) { + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(cfa) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i < M ; i++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + ix = 0; + for(j = 0 ; j < N ; j++) { + temp = mulc(Alpha , X[ix]); + iy = 0; + for(i = 0 ; i < M ; i++) { + Y[iy] = addc(Y[iy] , mulc(temp , A[i*rsa + j*csa])); + iy = iy + incy; + } + ix = ix + incx; + } + + return; +} + +double libblis_test_iaxpyf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + dim_t M = bli_obj_vector_dim( y ); + dim_t N = bli_obj_width( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + bool cfx = bli_obj_has_conj( x ); + bool cfa = bli_obj_has_conj( a ); + f77_int rsa = bli_obj_row_stride( a ) ; + f77_int csa = bli_obj_col_stride( a ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iaxpyf_check( M, N, Alpha, A, rsa, csa, + X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iaxpyf_check( M, N, Alpha, A, rsa, csa, + X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icaxpyf_check( M, N, Alpha, A, rsa, csa, + cfa, X, incx, cfx, Y, incy ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icaxpyf_check( M, N, Alpha, A, rsa, csa, + cfa, X, incx, cfx, Y, incy ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_axpym.cpp b/gtestsuite/src/ref_axpym.cpp new file mode 100644 index 000000000..83aa8bb79 --- /dev/null +++ b/gtestsuite/src/ref_axpym.cpp @@ -0,0 +1,137 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpym.h" + +using namespace std; + +//* ========================================================================== +//*> AXPYM performs matrix operations +//*> B := B + alpha * transa(A) +//*> where B is an m x n matrix. +//* ========================================================================== + +template +void libblis_iaxpym_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, T* Y, dim_t rsy, dim_t csy, T* YY) { + + dim_t i, j; + T Alpha = alpha[0]; + + if ((M == 0) || (N == 0)) { + return; + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = Y[i*rsy + j*csy] + ( Alpha * X[i*rsx + j*csx] ); + } + } + + return; +} + +template +void libblis_icaxpym_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, conj_t conjx, T* Y, dim_t rsy, dim_t csy) { + + dim_t i, j; + T Alpha = *alpha; + + if ((M == 0) || (N == 0)) { + return; + } + + if(conjx) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = conjugate(X[i*rsx + j*csx]); + } + } + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = addc(Y[i*rsy + j*csy] , mulc(Alpha , X[i*rsx + j*csx])); + } + } + + return; +} + +double libblis_test_iaxpym_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( x ); + bool transx = bli_obj_has_trans( x ); + conj_t conjx = bli_obj_conj_status( x ); + dim_t M = bli_obj_length( y ); + dim_t N = bli_obj_width( y ); + dim_t rsy = bli_obj_row_stride( y ) ; + dim_t csy = bli_obj_col_stride( y ) ; + double resid = 0.0; + dim_t rsx, csx; + + if( bli_obj_is_col_stored( x ) ) { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } else { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iaxpym_check( M, N, Alpha, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iaxpym_check( M, N, Alpha, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icaxpym_check( M, N, Alpha, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icaxpym_check( M, N, Alpha, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_axpyv.cpp b/gtestsuite/src/ref_axpyv.cpp new file mode 100644 index 000000000..6a63b31ff --- /dev/null +++ b/gtestsuite/src/ref_axpyv.cpp @@ -0,0 +1,160 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpyv.h" + +using namespace std; + +//* ========================================================================== +//*> AXPYV performs vector operations +//*> y := y + alpha * conjx(x) +//*> where x and y are vectors of length n, and alpha is a scalar +//* ========================================================================== + +template +void libblis_iaxpyv_check(dim_t len, T* alpha, T* X, dim_t incx, + T* Y, dim_t incy) { + + dim_t i, ix, iy; + T ONE, ZERO; + ONE = 1.0 ; + ZERO = 0.0 ; + T Alpha = alpha[0]; + + if (len == 0){ + return; + } + + if (Alpha != ONE) { + ix = 0; + if (Alpha == ZERO) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = Alpha*X[ix]; + ix = ix + incx; + } + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = Y[iy] + X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +template +void libblis_icaxpyv_check(dim_t len, T* alpha, T* X, dim_t incx, + T* Y, dim_t incy, bool cfx) { + dim_t i, ix, iy; + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + + if(len == 0){ + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + /* First form y := beta*y. */ + if (Alpha.real != ONE.real) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = mulc(Alpha , X[ix]); + ix = ix + incx; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = addc(Y[iy] , X[ix]); + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +double libblis_test_iaxpyv_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iaxpyv_check( M, Alpha, X, incx, + Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iaxpyv_check( M, Alpha, X, incx, + Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icaxpyv_check( M, Alpha, X, incx, + Y, incy, cfx ); + + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icaxpyv_check( M, Alpha, X, incx, + Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} diff --git a/gtestsuite/src/ref_copym.cpp b/gtestsuite/src/ref_copym.cpp new file mode 100644 index 000000000..1e99e451c --- /dev/null +++ b/gtestsuite/src/ref_copym.cpp @@ -0,0 +1,130 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_copym.h" + +using namespace std; + +//* ========================================================================== +//*> COPYM performs matrix operations +//*> B := transa(A) +//*> where B is an m x n matrix. +//* ========================================================================== + +template +void libblis_icopym_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx, + T* Y, dim_t rsy, dim_t csy, T* YY) { + + dim_t i, j; + + if ((M == 0) || (N == 0)) { + return; + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = X[i*rsx + j*csx]; + } + } + + return; +} + +template +void libblis_iccopym_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx, + conj_t conjx, T* Y, dim_t rsy, dim_t csy) { + + dim_t i, j; + + if ((M == 0) || (N == 0)) { + return; + } + + if(conjx) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = conjugate(X[i*rsx + j*csx]); + } + } + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = X[i*rsx + j*csx]; + } + } + + return; +} + +double libblis_test_icopym_check( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + bool transx = bli_obj_has_trans( x ); + conj_t conjx = bli_obj_conj_status( x ); + dim_t M = bli_obj_length( y ); + dim_t N = bli_obj_width( y ); + dim_t rsy = bli_obj_row_stride( y ) ; + dim_t csy = bli_obj_col_stride( y ) ; + double resid = 0.0; + dim_t rsx, csx; + + if( bli_obj_is_col_stored( x ) ) { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } else { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_icopym_check( M, N, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_icopym_check( M, N, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_iccopym_check( M, N, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_iccopym_check( M, N, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_copyv.cpp b/gtestsuite/src/ref_copyv.cpp new file mode 100644 index 000000000..867a8280c --- /dev/null +++ b/gtestsuite/src/ref_copyv.cpp @@ -0,0 +1,114 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_copyv.h" + +using namespace std; + +//* ========================================================================== +//*> COPYV performs vector operations +//*> y := conjx(x) +//*> where x and y are vectors of length n. +//* ========================================================================== + +template +void libblis_icopyv_check(dim_t len, T* X, dim_t incx, T* Y, dim_t incy) { + + dim_t i, ix, iy; + if (len == 0) { + return; + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +template +void libblis_iccopyv_check(dim_t len, T* X, dim_t incx, T* Y, + dim_t incy, bool cfx) { + dim_t i, ix, iy; + if (len == 0) { + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +double libblis_test_icopyv_check( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_icopyv_check( M, X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_icopyv_check( M, X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_iccopyv_check( M, X, incx, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_iccopyv_check( M, X, incx, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} diff --git a/gtestsuite/src/ref_dotaxpyv.cpp b/gtestsuite/src/ref_dotaxpyv.cpp new file mode 100644 index 000000000..6913b5369 --- /dev/null +++ b/gtestsuite/src/ref_dotaxpyv.cpp @@ -0,0 +1,228 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotaxpyv.h" + +using namespace std; + +//* ========================================================================== +//*> DOTAXPYV performs fused operations +//*> rho := conjxt(x^T) * conjy(y) +//*> y := y + alpha * conjx(x) +//*> where x, y, and z are vectors of length m and alpha and rho are scalars. +//*> The kernel is implemented as a fusion of calls to dotv and axpyv +//* ========================================================================== + +template +T libblis_idotaxpyv_check(dim_t len, T* alpha, T* XT, dim_t incxt, + T* X, dim_t incx, T* Y, dim_t incy, T* Z, dim_t incz ) { + + dim_t i, ixt, ix, iy, iz; + T ONE = 1.0 ; + T ZERO = 0.0 ; + T Alpha = alpha[0]; + T pr = 0.0; + if (len == 0) { + return pr; + } + + ixt = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + pr = pr + XT[ixt] * Y[iy]; + ixt = ixt + incxt; + iy = iy + incy; + } + + if (Alpha != ONE) { + ix = 0; + if (Alpha == ZERO) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = Alpha*X[ix]; + ix = ix + incx; + } + } + } + + ix = 0; + iz = 0; + for(i = 0 ; i < len ; i++) { + Z[iz] = Z[iz] + X[ix]; + ix = ix + incx; + iz = iz + incz; + } + + return pr; +} + +template +T libblis_icdotaxpyv_check(dim_t len, T* alpha, T* XT, dim_t incxt, bool cfxt, + T* X, dim_t incx, bool cfx, T* Y, dim_t incy, bool cfy, T* Z, dim_t incz ) { + + dim_t i, ixt, ix, iy, iz; + T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + + T pr = {0.0, 0.0}; + if (len == 0) { + return pr; + } + + if(cfxt) { + ixt = 0; + for(i = 0 ; i < len ; i++) { + XT[ixt] = conjugate(XT[ixt]); + ixt = ixt + incxt; + } + } + + if(cfy) { + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = conjugate(Y[iy]); + iy = iy + incy; + } + } + + ixt = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + pr = addc(pr, mulc(Y[iy] , XT[ixt])); + ixt = ixt + incxt; + iy = iy + incy; + } + + if(cfx != cfxt) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if ((Alpha.real != ONE.real) && (Alpha.imag != ONE.imag)) { + ix = 0; + if ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = mulc(Alpha, X[ix]); + ix = ix + incx; + } + } + } + + ix = 0; + iz = 0; + for(i = 0 ; i < len ; i++) { + Z[iz] = addc(Z[iz] , X[ix]); + ix = ix + incx; + iz = iz + incz; + } + + return pr; + +} + +double libblis_test_idotaxpyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho_orig, + obj_t* z, + obj_t* z_orig +) { + num_t dt = bli_obj_dt( y ); + dim_t M = bli_obj_vector_dim( z ); + f77_int incxt = bli_obj_vector_inc( xt ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int incz = bli_obj_vector_inc( z ); + bool cfxt = bli_obj_has_conj( xt ); + bool cfx = bli_obj_has_conj( x ); + bool cfy = bli_obj_has_conj( y ); + double r1,r2,resid ; + r1 = r2 = resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* XT = (float*) bli_obj_buffer( xt ); + float* Y = (float*) bli_obj_buffer( y ); + float* Z = (float*) bli_obj_buffer( z_orig ); + float* ZZ = (float*) bli_obj_buffer( z ); + float* av = (float*) bli_obj_buffer( rho_orig ); + float ref = libblis_idotaxpyv_check( M, Alpha, + XT, incxt, X, incx, Y, incy, Z, incz ); + r1 = computediffrv(M, incy, ZZ, Z); + r2 = (*av - ref); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* XT = (double*) bli_obj_buffer( xt ); + double* Y = (double*) bli_obj_buffer( y ); + double* Z = (double*) bli_obj_buffer( z_orig ); + double* ZZ = (double*) bli_obj_buffer( z ); + double* av = (double*) bli_obj_buffer( rho_orig ); + double ref = libblis_idotaxpyv_check( M, Alpha, + XT, incxt, X, incx, Y, incy, Z, incz ); + r1 = computediffrv(M, incy, ZZ, Z); + r2 = (*av - ref); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* XT = (scomplex*) bli_obj_buffer( xt ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* Z = (scomplex*) bli_obj_buffer( z_orig ); + scomplex* ZZ = (scomplex*) bli_obj_buffer( z ); + scomplex* av = (scomplex*) bli_obj_buffer( rho_orig ); + scomplex ref = libblis_icdotaxpyv_check( M, Alpha, + XT, incxt, cfxt, X, incx, cfx, Y, incy, cfy, Z, incz ); + r1 = computediffiv(M, incy, ZZ, Z); + r2 = ((*av).real - ref.real); + r2 +=((*av).imag - ref.imag); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* XT = (dcomplex*) bli_obj_buffer( xt ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* Z = (dcomplex*) bli_obj_buffer( z_orig ); + dcomplex* ZZ = (dcomplex*) bli_obj_buffer( z ); + dcomplex* av = (dcomplex*) bli_obj_buffer( rho_orig ); + dcomplex ref = libblis_icdotaxpyv_check( M, Alpha, + XT, incxt, cfxt, X, incx, cfx, Y, incy, cfy, Z, incz ); + r1 = computediffiv(M, incy, ZZ, Z); + r2 = ((*av).real - ref.real); + r2 +=((*av).imag - ref.imag); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + resid = abs(bli_fmaxabs( r1, r2 )); + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_dotv.cpp b/gtestsuite/src/ref_dotv.cpp new file mode 100644 index 000000000..8f6b8f474 --- /dev/null +++ b/gtestsuite/src/ref_dotv.cpp @@ -0,0 +1,129 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotv.h" + +using namespace std; + +//* ========================================================================== +//*> DOTV performs vector operations +//*> rho := conjx(x)^T * conjy(y) +//*> where x and y are vectors of length n, and rho is a scalar. +//* ========================================================================== + +template +T libblis_idotv_check(dim_t len, T* X, dim_t incx, T* Y, dim_t incy) { + + dim_t i, ix, iy; + T pr = 0.0; + if (len == 0) { + return pr; + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + pr = pr + X[ix] * Y[iy]; + ix = ix + incx; + iy = iy + incy; + } + + return pr; +} + +template +T libblis_icdotv_check(dim_t len, T* X, dim_t incx, T* Y, + dim_t incy, bool cfx, bool cfy) { + dim_t i, ix, iy; + T pr = {0.0, 0.0}; + if (len == 0) { + return pr; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(cfy) { + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = conjugate(Y[iy]); + iy = iy + incy; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + pr = addc(pr, mulc(Y[iy] , X[ix])); + ix = ix + incx; + iy = iy + incy; + } + + return pr; +} + +double libblis_test_idotv_check( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* rho +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + bool cfy = bli_obj_has_conj( y ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y ); + float* av = (float*) bli_obj_internal_scalar_buffer( rho ); + float ref = libblis_idotv_check( M, X, incx, Y, incy ); + resid = (*av - ref); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y ); + double* av = (double*) bli_obj_internal_scalar_buffer( rho ); + double ref = libblis_idotv_check( M, X, incx, Y, incy ); + resid = (*av - ref); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* av = (scomplex*) bli_obj_internal_scalar_buffer( rho ); + scomplex ref = libblis_icdotv_check( M, X, incx, + Y, incy, cfx, cfy ); + resid = ((*av).real - ref.real); + resid +=((*av).imag - ref.imag); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* av = (dcomplex*) bli_obj_internal_scalar_buffer( rho ); + dcomplex ref = libblis_icdotv_check( M, X, incx, + Y, incy, cfx, cfy ); + resid = ((*av).real - ref.real); + resid +=((*av).imag - ref.imag); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_dotxaxpyf.cpp b/gtestsuite/src/ref_dotxaxpyf.cpp new file mode 100644 index 000000000..68abe7386 --- /dev/null +++ b/gtestsuite/src/ref_dotxaxpyf.cpp @@ -0,0 +1,245 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotxaxpyf.h" + +using namespace std; + +//* ========================================================================== +//*> DOTXAXPYF performs fused operations +//*> y := beta * y + alpha * conjat(A^T) * conjw(w) +//*> z := z + alpha * conja(A) * conjx(x) +//*> where A is an m x b matrix, w and z are vectors of length m, +//*> x and y are vectors of length b, and alpha and beta are scalars. +//*> The kernel is implemented as a fusion of calls to dotxf and axpyf. +//* ========================================================================== + +template +void libblis_idotxaxpyf_check(dim_t M, dim_t N, T* alpha, T* AT, T* A, dim_t rsa, + dim_t csa, T* W, dim_t incw, T* X, dim_t incx, T* beta, T* Y, + dim_t incy, T* Z, dim_t incz) { + + dim_t i, j, iw, ix, iy, iz; + T Alpha = alpha[0]; + T Beta = beta[0]; + T temp; + if((M == 0) || (N == 0)) { + return; + } + + //y := beta * y + alpha * conjat(A^T) * conjw(w) + iy = 0; + for(j = 0 ; j < N ; j++) { + iw = 0; + temp = 0.0; + for(i = 0 ; i < M ; i++) { + temp += W[iw] * AT[i*rsa + j*csa]; + iw = iw + incw; + } + temp = Alpha * temp; + Y[iy] = (Y[iy] * Beta) + temp; + iy = iy + incy; + } + + //z := z + alpha * conja(A) * conjx(x) + ix = 0; + for(j = 0 ; j < N ; j++) { + temp = Alpha * X[ix]; + iz = 0; + for(i = 0 ; i < M ; i++) { + Z[iz] = Z[iz] + temp * A[i*rsa + j*csa]; + iz = iz + incz; + } + ix = ix + incx; + } + + return; +} + +template +void libblis_icdotxaxpyf_check(dim_t M, dim_t N, T* alpha, T* AT, bool conjat, + T* A, dim_t rsa, dim_t csa, bool conja, T* W, dim_t incw, bool conjw, T* X, + dim_t incx, bool conjx, T* beta, T* Y, dim_t incy, T* Z, dim_t incz) { + + dim_t i, j, iw, ix, iy, iz; + //T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + T Beta = *beta; + T temp; + + if((M == 0) || (N == 0)) { + return; + } + + if(conjw) { + iw = 0; + for(i = 0 ; i < M ; i++) { + W[iw] = conjugate(W[iw]); + iw = iw + incw; + } + } + + if(conjat) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i < M ; i++) { + AT[i*rsa + j*csa] = conjugate(AT[i*rsa + j*csa]); + } + } + } + + //y := beta * y + alpha * conjat(A^T) * conjw(w) + iy = 0; + for(j = 0 ; j < N ; j++) { + iw = 0; + temp = ZERO; + for(i = 0 ; i < M ; i++) { + temp = addc(temp , mulc(W[iw] , AT[i*rsa + j*csa])); + iw = iw + incw; + } + temp = mulc(Alpha , temp); + Y[iy] = addc(temp , mulc(Y[iy] , Beta)); + iy = iy + incy; + } + + //z := z + alpha * conja(A) * conjx(x) + if(conjx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(conja != conjat) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i < M ; i++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + //z := z + alpha * conja(A) * conjx(x) + ix = 0; + for(j = 0 ; j < N ; j++) { + temp = mulc(Alpha , X[ix]); + iz = 0; + for(i = 0 ; i < M ; i++) { + Z[iz] = addc(Z[iz] , mulc(temp , A[i*rsa + j*csa])); + iz = iz + incz; + } + ix = ix + incx; + } + + return; +} + +double libblis_test_idotxaxpyf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + obj_t* y_orig, + obj_t* z_orig +) { + num_t dt = bli_obj_dt( a ); + dim_t M = bli_obj_vector_dim( z ); + dim_t N = bli_obj_vector_dim( y ); + f77_int rsa = bli_obj_row_stride( a ) ; + f77_int csa = bli_obj_col_stride( a ) ; + f77_int incw = bli_obj_vector_inc( w ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int incz = bli_obj_vector_inc( z ); + bool conjat = bli_obj_has_conj( at ); + bool conja = bli_obj_has_conj( a ); + bool conjw = bli_obj_has_conj( w ); + bool conjx = bli_obj_has_conj( x ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* AT = (float*) bli_obj_buffer( at ); + float* A = (float*) bli_obj_buffer( a ); + float* W = (float*) bli_obj_buffer( w ); + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* Z = (float*) bli_obj_buffer( z_orig ); + float* YY = (float*) bli_obj_buffer( y ); + float* ZZ = (float*) bli_obj_buffer( z ); + libblis_idotxaxpyf_check(M, N, Alpha, AT, + A, rsa, csa, W, incw, X, incx, Beta, Y, incy, Z, incz); + resid = computediffrv(M, incz, ZZ, Z); + resid += computediffrv(N, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* AT = (double*) bli_obj_buffer( at ); + double* A = (double*) bli_obj_buffer( a ); + double* W = (double*) bli_obj_buffer( w ); + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* Z = (double*) bli_obj_buffer( z_orig ); + double* YY = (double*) bli_obj_buffer( y ); + double* ZZ = (double*) bli_obj_buffer( z ); + libblis_idotxaxpyf_check(M, N, Alpha, AT, + A, rsa, csa, W, incw, X, incx, Beta, Y, incy, Z, incz); + resid = computediffrv(M, incz, ZZ, Z); + resid += computediffrv(N, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* AT = (scomplex*) bli_obj_buffer( at ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* W = (scomplex*) bli_obj_buffer( w ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* Z = (scomplex*) bli_obj_buffer( z_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + scomplex* ZZ = (scomplex*) bli_obj_buffer( z ); + libblis_icdotxaxpyf_check(M, N, Alpha, AT, conjat, + A, rsa, csa, conja, W, incw, conjw, X, incx, conjx, Beta, + Y, incy, Z, incz); + resid = computediffiv(M, incz, ZZ, Z); + resid += computediffiv(N, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* AT = (dcomplex*) bli_obj_buffer( at ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* W = (dcomplex*) bli_obj_buffer( w ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* Z = (dcomplex*) bli_obj_buffer( z_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + dcomplex* ZZ = (dcomplex*) bli_obj_buffer( z ); + libblis_icdotxaxpyf_check(M, N, Alpha, AT, conjat, + A, rsa, csa, conja, W, incw, conjw, X, incx, conjx, Beta, + Y, incy, Z, incz); + resid = computediffiv(M, incz, ZZ, Z); + resid += computediffiv(N, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return abs(resid); +} + diff --git a/gtestsuite/src/ref_dotxf.cpp b/gtestsuite/src/ref_dotxf.cpp new file mode 100644 index 000000000..337bdea7e --- /dev/null +++ b/gtestsuite/src/ref_dotxf.cpp @@ -0,0 +1,172 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotxf.h" + +using namespace std; + +//* ========================================================================== +//*> DOTXF performs vector operations +//*> y := beta * y_orig + alpha * conjat(A^T) * conjx(x) +//*> where A is an m x b matrix, and y and x are vectors. +//*> The kernel is implemented as a fused series of calls to dotxv +//*> where b is less than or equal to an implementation-dependent fusing +//*> factor specific to dotxf +//* ========================================================================== + +template +void libblis_idotxf_check(dim_t M, dim_t N, T* alpha, T* A, dim_t rsa, + dim_t csa, T* X, dim_t incx, T* beta, T* Y, dim_t incy) { + + dim_t i, j, ix, iy; + T Alpha = alpha[0]; + T Beta = beta[0]; + T temp; + if((M == 0) || (N == 0)) { + return; + } + + iy = 0; + for(j = 0 ; j < M ; j++) { + ix = 0; + temp = 0.0; + for(i = 0 ; i < N ; i++) { + temp += X[ix] * A[i*rsa + j*csa]; + ix = ix + incx; + } + temp = Alpha * temp; + Y[iy] = (Y[iy] * Beta) + temp; + iy = iy + incy; + } + + return; +} + +template +void libblis_icdotxf_check(dim_t M, dim_t N, T* alpha, T* A, dim_t rsa, +dim_t csa, bool cfa, T* X, dim_t incx, bool cfx, T* beta, T* Y, dim_t incy ) { + + dim_t i, j, ix, iy; + //T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + T Beta = *beta; + T temp; + + if((M == 0) || (N == 0)) { + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(cfa) { + for(j = 0 ; j < M ; j++) { + for(i = 0 ; i < N ; i++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + iy = 0; + for(j = 0 ; j < M ; j++) { + ix = 0; + temp = ZERO; + for(i = 0 ; i < N ; i++) { + temp = addc(temp , mulc(X[ix] , A[i*rsa + j*csa])); + ix = ix + incx; + } + temp = mulc(Alpha , temp); + Y[iy] = addc(temp , mulc(Y[iy] , Beta)); + iy = iy + incy; + } + + return; +} + +double libblis_test_idotxf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + bool cfx = bli_obj_has_conj( x ); + bool cfa = bli_obj_has_conj( a ); + double resid = 0.0; + + //martix transpose + dim_t N = bli_obj_vector_dim( x ); + dim_t M = bli_obj_vector_dim( y ); + f77_int rsa = bli_obj_row_stride( a ); + f77_int csa = bli_obj_col_stride( a ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_idotxf_check( M, N, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_idotxf_check( M, N, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icdotxf_check( M, N, Alpha, A, rsa, csa, + cfa, X, incx, cfx, Beta, Y, incy ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icdotxf_check( M, N, Alpha, A, rsa, csa, + cfa, X, incx, cfx, Beta, Y, incy ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_dotxv.cpp b/gtestsuite/src/ref_dotxv.cpp new file mode 100644 index 000000000..06a303abd --- /dev/null +++ b/gtestsuite/src/ref_dotxv.cpp @@ -0,0 +1,197 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotxv.h" + +using namespace std; + +//* ========================================================================== +//*> DOTXV performs vector operations +//*> rho := beta * rho + alpha * conjx(x)^T * conjy(y) +//*> where x and y are vectors of length n, and alpha, beta, and rho are scalars. +//* ========================================================================== + +template +void libblis_idotxv_check(dim_t len, T* alpha, T* X, dim_t incx, + T* beta, T* Y, dim_t incy, T* rhorig ) { + dim_t i, ix, iy; + T ONE, ZERO; + ONE = 1.0 ; + ZERO = 0.0 ; + T Alpha = alpha[0]; + T Beta = beta[0]; + T rho = *rhorig; + + if(len == 0) { + return; + } + + rho = rho * Beta; + + if (Alpha != ONE) { + ix = 0; + if (Alpha == ZERO) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = Alpha * X[ix]; + ix = ix + incx; + } + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + rho = rho + X[ix] * Y[iy]; + ix = ix + incx; + iy = iy + incy; + } + + *rhorig = rho; + return; +} + +template +void libblis_icdotxv_check(dim_t len, T* alpha, T* X, dim_t incx, T* beta, + T* Y, dim_t incy, T* rhorig, bool cfx, bool cfy) { + dim_t i, ix, iy; + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + T Beta = *beta; + T rho = *rhorig; + + if (len == 0) { + return; + } + + rho = mulc(rho , Beta); + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if((Alpha.real != ONE.real) && (Alpha.imag != ONE.imag)) { + ix = 0; + if((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) { + for(i = 0 ; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for(i = 0 ; i < len ; i++) { + X[ix] = mulc(Alpha , X[ix]); + ix = ix + incx; + } + } + } + + if(cfy) { + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = conjugate(Y[iy]); + iy = iy + incy; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + rho = addc(rho, mulc(Y[iy] , X[ix])); + ix = ix + incx; + iy = iy + incy; + } + + *rhorig = rho; + return; +} + +double libblis_test_idotxv_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho, + obj_t* rho_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + bool cfy = bli_obj_has_conj( y ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y ); + float* rhorig = (float*) bli_obj_internal_scalar_buffer( rho_orig ); + float* rhp = (float*) bli_obj_internal_scalar_buffer( rho ); + libblis_idotxv_check( M, Alpha, X, incx, + Beta, Y, incy, rhorig ); + resid = (*rhp - *rhorig); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y ); + double* rhorig = (double*) bli_obj_internal_scalar_buffer( rho_orig ); + double* rhp = (double*) bli_obj_internal_scalar_buffer( rho ); + libblis_idotxv_check( M, Alpha, X, incx, + Beta, Y, incy, rhorig ); + resid = (*rhp - *rhorig); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* rhorig = (scomplex*) bli_obj_internal_scalar_buffer( rho_orig ); + scomplex* rhp = (scomplex*) bli_obj_internal_scalar_buffer( rho ); + libblis_icdotxv_check( M, Alpha, X, incx, + Beta, Y, incy, rhorig, cfx, cfy ); + resid = ((*rhp).real - (*rhorig).real); + resid += ((*rhp).imag - (*rhorig).imag); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* rhorig = (dcomplex*) bli_obj_internal_scalar_buffer( rho_orig ); + dcomplex* rhp = (dcomplex*) bli_obj_internal_scalar_buffer( rho ); + libblis_icdotxv_check( M, Alpha, X, incx, + Beta, Y, incy, rhorig, cfx, cfy ); + resid = ((*rhp).real - (*rhorig).real); + resid += ((*rhp).imag - (*rhorig).imag); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return abs(resid); +} + diff --git a/gtestsuite/src/ref_gemm.cpp b/gtestsuite/src/ref_gemm.cpp new file mode 100644 index 000000000..99938ae3d --- /dev/null +++ b/gtestsuite/src/ref_gemm.cpp @@ -0,0 +1,317 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_gemm.h" + +using namespace std; + +//* ========================================================================== +//*> GEMM performs one of the matrix-matrix operations +//*> C := alpha*op( A )*op( B ) + beta*C, +//*> where op( X ) is one of +//*> op( X ) = X or op( X ) = X**T or op( X ) = X**H, +//*> alpha and beta are scalars, and A, B and C are matrices, with op( A ) +//*> an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. +/* +Reference GEMM implemenation C = C*Beta + Alpha*A*B +Row major A=mxk , B=kxn and C=mxn , lda=rsa=k, csa=1, ldb=rsb=n,csb=1, ldc=rsc=n,csc=1 +Col major A=mxk , B=kxn and C=mxn , rsa=1, lda=csa=m, rsb=1,ldb=csb=k, rsc=1,ldc=csc=m +*/ +//* ========================================================================== + +template +void libblis_igemm_check(dim_t M, dim_t N, dim_t K, T *alpha, T *A, + dim_t rsa, dim_t csa, T *B, dim_t rsb, dim_t csb, T* beta, + T *C, dim_t rsc, dim_t csc){ + + T Alpha = alpha[0]; + T Beta = beta[0]; + int i,j,k; + + if(( Alpha != 0.) && ( Beta != 0. )) { + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + T sum = 0.0; + for( k = 0 ; k < K ; k++ ) { + sum += A[i*rsa + k*csa] * B[k*rsb + j*csb]; + } + sum = ((Beta * C[i*rsc + j*csc]) + (Alpha * sum)); + C[i*rsc + j*csc] = sum; + } + } + } + else if(( Alpha != 0.) && ( Beta == 0. )) { + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + T sum = 0.0; + for( k = 0 ; k < K ; k++ ) { + sum += A[i*rsa + k*csa] * B[k*rsb + j*csb]; + } + sum = (Alpha * sum); + C[i*rsc + j*csc] = sum; + } + } + } + else if(( Alpha == 0.) && ( Beta != 0. )) { + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + T sum = (Beta * C[ i*rsc + j*csc ]); + C[i*rsc + j*csc] = sum; + } + } + } + else /*if(( Alpha == 0.) && ( Beta == 0. ))*/ { + // + } + + return; +} + +template +void libblis_icgemm_check(dim_t M, dim_t N, dim_t K, T *alpha, + T *A, dim_t rsa, dim_t csa, bool conja, T *B, dim_t rsb, dim_t csb, + bool conjb, T* beta, T *C, dim_t rsc, dim_t csc){ + + T Alpha = *alpha; + T Beta = *beta; + int i,j,k; + + if(conja) { + for( i = 0 ; i < M ; i++ ) { + for( k = 0 ; k < K ; k++ ) { + A[i*rsa + k*csa] = conjugate(A[i*rsa + k*csa]); + } + } + } + if(conjb) { + for( k = 0 ; k < K ; k++ ) { + for( j = 0 ; j < N ; j++ ) { + B[k*rsb + j*csb] = conjugate(B[k*rsb + j*csb]); + } + } + } + + if((Alpha.real != 0.) && (Beta.real != 0.)) { + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + T sum = {0.0, 0.0}; + for( k = 0 ; k < K ; k++ ) { + T aa = A[i*rsa + k*csa]; + T bb = B[k*rsb + j*csb]; + sum = addc(sum , mulc(aa , bb)); + } + T xc = C[i*rsc + j*csc]; + sum = mulc(Alpha,sum); + xc = mulc(Beta,xc); + C[i*rsc + j*csc] = addc(xc , sum); + } + } + } + else if(( Alpha.real != 0.) && ( Beta.real == 0. )) { + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + T sum = {0.0, 0.0}; + for( k = 0 ; k < K ; k++ ) { + T aa = A[i*rsa + k*csa]; + T bb = B[k*rsb + j*csb]; + sum = addc(sum , mulc(aa , bb)); + } + sum = mulc(Alpha,sum); + C[i*rsc + j*csc] = sum; + } + } + } + else if(( Alpha.real == 0.) && ( Beta.real != 0. )) { + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + T sum ; + T cc = C[i*rsc + j*csc]; + sum = mulc(Beta,cc); + C[i*rsc + j*csc] = sum; + } + } + } else /*if(( Alpha == 0.) && ( Beta == 0. ))*/ { + // + } + return ; +} + +double libblis_test_igemm_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + num_t dt +){ + dim_t M = bli_obj_length( c_orig ); + dim_t N = bli_obj_width( c_orig ); + dim_t K = bli_obj_width_after_trans( a ); + dim_t rsa, csa; + dim_t rsb, csb; + dim_t rsc, csc; + bool conja = bli_obj_has_conj( a ); + bool conjb = bli_obj_has_conj( b ); + trans_t transA = bli_obj_onlytrans_status( a ); + trans_t transB = bli_obj_onlytrans_status( b ); + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsa = transA ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = transA ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + rsb = transB ? bli_obj_col_stride( b ) : bli_obj_row_stride( b ) ; + csb = transB ? bli_obj_row_stride( b ) : bli_obj_col_stride( b ) ; + rsc = 1; + csc = bli_obj_col_stride( c_orig ); + } else { + rsa = transA ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = transA ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + rsb = transB ? bli_obj_col_stride( b ) : bli_obj_row_stride( b ) ; + csb = transB ? bli_obj_row_stride( b ) : bli_obj_col_stride( b ) ; + rsc = bli_obj_row_stride( c_orig ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_igemm_check(M, N, K, Alpha, A, rsa, csa, + B, rsb, csb, Beta, C, rsc, csc); + resid = computediffrm(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_igemm_check(M, N, K, Alpha, A, rsa, csa, + B, rsb, csb, Beta, C, rsc, csc); + resid = computediffrm(M, N, CC, C, rsc, csc); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + libblis_icgemm_check(M, N, K, Alpha, A, rsa, csa, + conja, B, rsb, csb, conjb, Beta, C, rsc, csc); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + libblis_icgemm_check(M, N, K, Alpha, A, rsa, csa, + conja, B, rsb, csb, conjb, Beta, C, rsc, csc); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + +template +double libblis_check_nan_real( dim_t rsc, dim_t csc, obj_t* c ) { + + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + T* C = (T*) bli_obj_buffer( c ); + double resid = 0.0; + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rsc, dim_t csc, obj_t* c ) { + + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + T* C = (T*) bli_obj_buffer( c ); + double resid = 0.0; + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_gemm(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_gemmt.cpp b/gtestsuite/src/ref_gemmt.cpp new file mode 100644 index 000000000..59a194a97 --- /dev/null +++ b/gtestsuite/src/ref_gemmt.cpp @@ -0,0 +1,445 @@ +#include +#include "blis_test.h" +#include "blis_utils.h" +#include "test_gemmt.h" + +using namespace std; + +//* ========================================================================== +//*> GEMMT performs one of the matrix-matrix operations +//*> C := beta * C + alpha * transa(A) * transb(B) +//* ========================================================================== + +void libblis_gemv_check(trans_t transA , dim_t M, dim_t N, float* Alpha, + float* A, dim_t rsa, dim_t csa, bool conja, float* X, dim_t incx, bool conjx, + float* Beta, float* Y, dim_t incy) { + libblis_igemv_check(transA, M, N, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy); + return; +} + +void libblis_gemv_check(trans_t transA , dim_t M, dim_t N, double* Alpha, + double* A, dim_t rsa, dim_t csa, bool conja, double* X, dim_t incx, + bool conjx, double* Beta, double* Y, dim_t incy) { + libblis_igemv_check(transA, M, N, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy); + return; +} + +void libblis_gemv_check(trans_t transA , dim_t M, dim_t N, scomplex* Alpha, + scomplex* A, dim_t rsa, dim_t csa, bool conja, scomplex* X, dim_t incx, + bool conjx, scomplex* Beta, scomplex* Y, dim_t incy) { + libblis_icgemv_check(transA, M, N, Alpha, A, rsa, csa, + conja, X, incx, Beta, Y, incy, conjx); + return; +} +void libblis_gemv_check(trans_t transA , dim_t M, dim_t N, dcomplex* Alpha, + dcomplex* A, dim_t rsa, dim_t csa, bool conja, dcomplex* X, dim_t incx, + bool conjx, dcomplex* Beta, dcomplex* Y, dim_t incy) { + libblis_icgemv_check(transA, M, N, Alpha, A, rsa, csa, + conja, X, incx, Beta, Y, incy, conjx); + return; +} + + +void libblis_gemm_check(dim_t M, dim_t N, dim_t K, float* Alpha, float* A, + dim_t rsa, dim_t csa, bool conja, float* B, dim_t rsb, dim_t csb, bool conjb, + float* Beta, float* C, dim_t rsc, dim_t csc) { + libblis_igemm_check(M, N, K, Alpha, A, rsa, csa, B, rsb, + csb, Beta, C, rsc, csc); + return; +} + +void libblis_gemm_check(dim_t M, dim_t N, dim_t K, double* Alpha, double* A, + dim_t rsa, dim_t csa, bool conja, double* B, dim_t rsb, dim_t csb, bool conjb, + double* Beta, double* C, dim_t rsc, dim_t csc) { + libblis_igemm_check(M, N, K, Alpha, A, rsa, csa, B, rsb, + csb, Beta, C, rsc, csc); + return; +} + +void libblis_gemm_check(dim_t M, dim_t N, dim_t K, scomplex* Alpha, scomplex* A, + dim_t rsa, dim_t csa, bool conja, scomplex* B, dim_t rsb, dim_t csb, bool conjb, + scomplex* Beta, scomplex* C, dim_t rsc, dim_t csc) { + libblis_icgemm_check(M, N, K, Alpha, A, rsa, csa, conja, + B, rsb, csb, conjb, Beta, C, rsc, csc); + return; +} + +void libblis_gemm_check(dim_t M, dim_t N, dim_t K, dcomplex* Alpha, dcomplex* A, + dim_t rsa, dim_t csa, bool conja, dcomplex* B, dim_t rsb, dim_t csb, bool conjb, + dcomplex* Beta, dcomplex* C, dim_t rsc, dim_t csc) { + libblis_icgemm_check(M, N, K, Alpha, A, rsa, csa, conja, + B, rsb, csb, conjb, Beta, C, rsc, csc); + return; +} + +#define CROSSOVER_GEMMT 24 + +dim_t rec_split(dim_t n, num_t dt) { + dim_t res = 0; + + switch( dt ) { + case BLIS_FLOAT : + { + res = ((n >= 32) ? ((n + 16) / 32) * 16 : n / 2); + break; + } + case BLIS_DOUBLE : + { + res = ((n >= 16) ? ((n + 8) / 16) * 8 : n / 2); + break; + } + case BLIS_SCOMPLEX : + { + res = ((n >= 16) ? ((n + 8) / 16) * 8 : n / 2); + break; + } + case BLIS_DCOMPLEX : + { + res = ((n >= 8) ? ((n + 4) / 8) * 4 : n / 2); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return res; +} + +/** sgemmt's unblocked compute kernel */ +template +static void gemmt_rec2(uplo_t uploc, trans_t transA, trans_t transB, + dim_t n, dim_t k, T* alpha, T* A, dim_t ldA, bool conja, T* B, + dim_t ldB, bool conjb, T* beta, T* C, dim_t ldC ) { + + dim_t incB, incC; + dim_t rsa, csa; + dim_t i; + + rsa = (transA == BLIS_NO_TRANSPOSE) ? 1 : ldA ; + csa = (transA == BLIS_NO_TRANSPOSE) ? ldA : 1 ; + incB = (transB == BLIS_NO_TRANSPOSE) ? 1 : ldB ; + incC = 1; + + for (i = 0; i < n; i++) { + // A_0 + // A_i + T * A_0 = A; + T * A_i = A + ((transA == BLIS_NO_TRANSPOSE) ? i : ldA * i); + + // * B_i * + T * B_i = B + ((transB == BLIS_NO_TRANSPOSE) ? ldB * i : i); + + // * C_0i * + // * C_ii * + T * C_0i = C + ldC * i; + T * C_ii = C + ldC * i + i; + + if (uploc == BLIS_LOWER) { + int nmi = n - i; + if (transA == BLIS_NO_TRANSPOSE) + libblis_gemv_check(transA, nmi, k, alpha, A_i, rsa, csa, conja, B_i, incB, conjb, beta, C_ii, incC); + else + libblis_gemv_check(transA, k, nmi, alpha, A_i, rsa, csa, conja, B_i, incB, conjb, beta, C_ii, incC); + } else { + int ip1 = i + 1; + if (transA == BLIS_NO_TRANSPOSE) + libblis_gemv_check(transA, ip1, k, alpha, A_0, rsa, csa, conja, B_i, incB, conjb, beta, C_0i, incC); + else + libblis_gemv_check(transA, k, ip1, alpha, A_0, rsa, csa, conja, B_i, incB, conjb, beta, C_0i, incC); + } + } +} + +/** sgemmt's recursive compute kernel */ +template +static void gemmt_rec(uplo_t uploc, trans_t transA, trans_t transB, + dim_t n, dim_t k, T* alpha, T* A, dim_t ldA, bool cfA, T* B, + dim_t ldB, bool cfB, T* beta, T* C, dim_t ldC, num_t dt ) { + if (n <= max(CROSSOVER_GEMMT, 1)) { + // Unblocked + gemmt_rec2(uploc, transA, transB, n, k, alpha, A, ldA, cfA, + B, ldB, cfB, beta, C, ldC); + return; + } + + dim_t rsa, csa; + dim_t rsb, csb; + dim_t rsc, csc; + + rsa = (transA == BLIS_NO_TRANSPOSE) ? 1 : ldA ; + csa = (transA == BLIS_NO_TRANSPOSE) ? ldA : 1 ; + rsb = (transB == BLIS_NO_TRANSPOSE) ? 1 : ldB ; + csb = (transB == BLIS_NO_TRANSPOSE) ? ldB : 1 ; + rsc = 1 ; + csc = ldC; + + // Splitting + dim_t n1 = rec_split(n, dt); //SREC_SPLIT(n); + dim_t n2 = n - n1; + + // A_T + // A_B + T * A_T = A; + T * A_B = A + ((transA == BLIS_NO_TRANSPOSE) ? n1 : ldA * n1); + + // B_L B_R + T * B_L = B; + T * B_R = B + ((transB == BLIS_NO_TRANSPOSE) ? ldB * n1 : n1); + + // C_TL C_TR + // C_BL C_BR + T * C_TL = C; + T * C_TR = C + ldC * n1; + T * C_BL = C + n1; + T * C_BR = C + ldC * n1 + n1; + + // recursion(C_TL) + gemmt_rec(uploc, transA, transB, n1, k, alpha, A_T, ldA, cfA, B_L, ldB, + cfB, beta, C_TL, ldC, dt); + + if (uploc == BLIS_LOWER) + // C_BL = alpha A_B B_L + beta C_BL + libblis_gemm_check(n2, n1, k, alpha, A_B, rsa, csa, cfA, + B_L, rsb, csb, cfB, beta, C_BL, rsc, csc); + else + // C_TR = alpha A_T B_R + beta C_TR + libblis_gemm_check(n1, n2, k, alpha, A_T, rsa, csa, cfA, + B_R, rsb, csb, cfB, beta, C_TR, rsc, csc); + + // recursion(C_BR) + gemmt_rec(uploc, transA, transB, n2, k, alpha, A_B, ldA, cfA, B_R, ldB, + cfB, beta, C_BR, ldC, dt); +} + +double computediff(dim_t n,dim_t k, float *act, float *ref, dim_t rsc, dim_t csc) { + return computediffrm(n, k, act, ref, rsc, csc); +} + +double computediff(dim_t n,dim_t k, double *act, double *ref, dim_t rsc, dim_t csc) { + return computediffrm(n, k, act, ref, rsc, csc); +} + +double computediff(dim_t n,dim_t k, scomplex *act, scomplex *ref, dim_t rsc, dim_t csc) { + return computediffim(n, k, act, ref, rsc, csc); +} +double computediff(dim_t n,dim_t k, dcomplex *act, dcomplex *ref, dim_t rsc, dim_t csc) { + return computediffim(n, k, act, ref, rsc, csc); +} + +/** GEMMT computes a matrix-matrix product with general matrices but updates + * only the upper or lower triangular part of the result matrix. + * */ +template +double libblis_igemmt_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + num_t dt +){ + dim_t k = bli_obj_width_after_trans( a ); + dim_t n = bli_obj_width( c ); + uplo_t uploc = bli_obj_uplo( c ); + trans_t transA = bli_obj_onlytrans_status( a ); + trans_t transB = bli_obj_onlytrans_status( b ); + dim_t lda, ldb, ldc; + dim_t rsa, csa; + dim_t rsb, csb; + dim_t rsc, csc; + + bool crsf = bli_obj_is_row_stored( c ); + + if ( crsf ) { + rsa = transA ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = transA ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + rsb = transB ? bli_obj_col_stride( b ) : bli_obj_row_stride( b ) ; + csb = transB ? bli_obj_row_stride( b ) : bli_obj_col_stride( b ) ; + rsc = bli_obj_row_stride( c_orig ) ; + csc = 1 ; + lda = transA ? csa : rsa ; + ldb = transB ? csb : rsb ; + ldc = rsc ; + } else { + rsa = transA ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = transA ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + rsb = transB ? bli_obj_col_stride( b ) : bli_obj_row_stride( b ) ; + csb = transB ? bli_obj_row_stride( b ) : bli_obj_col_stride( b ) ; + rsc = 1 ; + csc = bli_obj_col_stride( c_orig ) ; ; + lda = transA ? rsa : csa ; + ldb = transB ? rsb : csb ; + ldc = csc ; + } + + T* A = (T*) bli_obj_buffer( a ); + T* B = (T*) bli_obj_buffer( b ); + T* C = (T*) bli_obj_buffer( c_orig ); + T* Alpha = (T*) bli_obj_buffer( alpha ); + T* Beta = (T*) bli_obj_buffer( beta ); + bool conja = bli_obj_has_conj( a ); + bool conjb = bli_obj_has_conj( b ); + + if(bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transA = bli_obj_onlytrans_status( a ); + conja = false; + } + + if(bli_obj_has_conj(b)) { + conjugate_tensor(b, dt); + transB = bli_obj_onlytrans_status( b ); + conjb = false; + } + + // Recursive kernel + if( !crsf ) { + gemmt_rec(uploc, transA, transB, n, k, Alpha, A, lda, + conja, B, ldb, conjb, Beta, C, ldc, dt); + }else { + if( uploc == BLIS_UPPER) + uploc = BLIS_LOWER; + else if(uploc == BLIS_LOWER) + uploc = BLIS_UPPER; + + gemmt_rec(uploc, transB, transA, n, k, Alpha, B, ldb, + conjb, A, lda, conja, Beta, C, ldc, dt); + } + + T* CC = (T*) bli_obj_buffer( c ); + + double resid = 0.0; + resid = computediff(n, k, C, CC, rsc, csc); + + return resid; +} + +double libblis_test_igemmt_check( + test_params_t *params, + obj_t *alpha, + obj_t *a, + obj_t *b, + obj_t *beta, + obj_t *c, + obj_t *c_orig +) { + double resid = 0.0; + num_t dt = bli_obj_dt(c); + + switch( dt ) { + case BLIS_FLOAT : + { + resid = libblis_igemmt_check( params, alpha, a, b, beta, + c, c_orig, dt ); + break; + } + case BLIS_DOUBLE : + { + resid = libblis_igemmt_check( params, alpha, a, b, beta, + c, c_orig, dt ); + break; + } + case BLIS_SCOMPLEX : + { + resid = libblis_igemmt_check( params, alpha, a, b, beta, + c, c_orig, dt ); + break; + } + case BLIS_DCOMPLEX : + { + resid = libblis_igemmt_check( params, alpha, a, b, beta, + c, c_orig, dt ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + + +template +double libblis_check_nan_real( dim_t rsc, dim_t csc, obj_t* c ) { + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + double resid = 0.0; + T* C = (T*) bli_obj_buffer( c ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rsc, dim_t csc, obj_t* c ) { + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + double resid = 0.0; + U* C = (U*) bli_obj_buffer( c ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_gemmt(obj_t* c) { + dim_t rsc, csc; + double resid = 0.0; + + num_t dt = bli_obj_dt(c); + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + + diff --git a/gtestsuite/src/ref_gemv.cpp b/gtestsuite/src/ref_gemv.cpp new file mode 100644 index 000000000..8ae50475b --- /dev/null +++ b/gtestsuite/src/ref_gemv.cpp @@ -0,0 +1,319 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_gemv.h" + +using namespace std; + +//* ========================================================================== +//*> GEMV performs one of the matrix-vector operations +//*> y := alpha*A*x + beta*y, or y := alpha*A**T*x + beta*y, or +//*> y := alpha*A**H*x + beta*y, +//* ========================================================================== + +template +void libblis_igemv_check(trans_t transA , dim_t M, dim_t N, T* alpha, T* A, + dim_t rsa, dim_t csa, T* X, dim_t incx, T* beta, T* Y, dim_t incy) { + T ONE, ZERO; + T temp; + dim_t i, ix, iy, j, jx, jy, kx, ky, lenx, leny; + bool NOTRANSA; + + ONE = 1.0 ; + ZERO = 0.0 ; + T Alpha = alpha[0]; + T Beta = beta[0]; + + if (((M == 0) || (N == 0)) || + ((Alpha == ZERO) && (Beta == ONE))) { + return; + } + + NOTRANSA = ((transA == BLIS_NO_TRANSPOSE) || (transA == BLIS_CONJ_NO_TRANSPOSE)); + + /* Set lenx and leny, the lengths of the vectors x and y, + and set up the start points in X and Y. */ + if (NOTRANSA) { + lenx = N; + leny = M; + } + else { + lenx = M; + leny = N; + } + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (lenx - 1) * incx; + } + + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (leny - 1) * incy; + } + + //* Start the operations. Here, the elements of A are + //* accessed sequentially with one pass through A. + //* First form y := beta*y. + if (Beta != ONE) { + iy = ky; + if (Beta == ZERO) { + for(i = 0 ; i < leny ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < leny ; i++) { + Y[iy] = Beta*Y[iy]; + iy = iy + incy; + } + } + } + + if(Alpha == ZERO) + return; + + if(NOTRANSA) { + /* Form y := alpha*A*x + y.*/ + jx = kx; + for(j = 0 ; j < N ; j++) { + temp = Alpha*X[jx]; + iy = ky; + for(i = 0 ; i < M ; i++) { + Y[iy] = Y[iy] + temp * A[i*rsa + j*csa]; + iy = iy + incy; + } + jx = jx + incx; + } + } + else { + //* Form y := alpha*A**T*x + y. + jy = ky; + for(i = 0 ; i < N ; i++) { + temp = ZERO; + ix = kx; + for(j = 0 ; j < M ; j++) { + temp = temp + A[i*rsa + j*csa] * X[ix]; + ix = ix + incx; + } + Y[jy] = Y[jy] + Alpha*temp; + jy = jy + incy; + } + } + return; +} + +template +void libblis_icgemv_check(trans_t transA , dim_t M, dim_t N, T* alpha, T* A, + dim_t rsa, dim_t csa, bool conja, T* X, dim_t incx, T* beta, T* Y, + dim_t incy, bool conjx) { + T ONE; + T ZERO; + T temp; + dim_t i, ix, iy, j, jx, jy, kx, ky, lenx, leny; + bool NOTRANSA; + + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + T Beta = *beta; + + if (((M == 0) || (N == 0)) || + ((Alpha.real == ZERO.real) && (Beta.real == ONE.real))) { + return ; + } + + NOTRANSA = ((transA == BLIS_NO_TRANSPOSE) || (transA == BLIS_CONJ_NO_TRANSPOSE)); + + /* Set lenx and leny, the lengths of the vectors x and y, + and set up the start points in X and Y. */ + if(NOTRANSA) { + lenx = N; + leny = M; + } + else { + lenx = M; + leny = N; + } + + if (incx > 0) { + kx = 0; + } + else{ + kx = 1 - (lenx - 1) * incx; + } + + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (leny - 1)*incy; + } + + if (Alpha.real == ZERO.real) + return; + + if( conja ) { + for(i = 0; i < leny ; i++) { + for(j = 0 ; j < lenx ; j++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + if(conjx) { + ix = kx; + for(i = 0 ; i < lenx ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + /* First form y := beta*y. */ + if (Beta.real != ONE.real) { + iy = ky; + if (Beta.real == ZERO.real) { + for(i = 0; i < leny ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < leny ; i++) { + Y[iy] = mulc(Beta , Y[iy]); + iy = iy + incy; + } + } + } + + if (NOTRANSA) { + /* Form y := alpha*A*x + y. */ + jx = kx; + for(j = 0; j < N ; j++) { + temp = mulc(Alpha , X[jx]); + iy = ky; + for(i = 0; i < M ; i++) { + Y[iy] = addc(Y[iy] , mulc(temp , A[i*rsa + j*csa])); + iy = iy + incy; + } + jx = jx + incx; + } + } + else { + /* Form y := alpha*A**T*x + y or y := alpha*A**H*x + y. */ + jy = ky; + for(i = 0 ; i < N ; i++) { + temp = ZERO; + ix = kx; + for(j = 0 ; j < M ; j++) { + temp = addc(temp , mulc(A[i*rsa + j*csa] , X[ix])); + ix = ix + incx; + } + Y[jy] = addc(Y[jy] , mulc(Alpha , temp)); + jy = jy + incy; + } + } + return; +} + +double libblis_test_igemv_check( + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + num_t dt +){ + double resid = 0.0; + f77_int rsa, csa; + trans_t transA = bli_obj_onlytrans_status( a ); + f77_int M = transA ? bli_obj_vector_dim( x ) : bli_obj_vector_dim( y_orig ); + f77_int N = transA ? bli_obj_vector_dim( y_orig ): bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + f77_int len = bli_obj_vector_dim( y_orig ); + bool cfx = bli_obj_has_conj( x ); + bool cfa = bli_obj_has_conj( a ); + bool sf = bli_obj_is_col_stored( a ); + + if( sf ) { + rsa = bli_obj_has_trans( a ) ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = bli_obj_has_trans( a ) ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + } else { + rsa = bli_obj_has_trans( a ) ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = bli_obj_has_trans( a ) ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + + if(transA == BLIS_NO_TRANSPOSE) transA = BLIS_TRANSPOSE; + else if(transA == BLIS_TRANSPOSE) transA = BLIS_NO_TRANSPOSE; + else if ( transA == BLIS_CONJ_NO_TRANSPOSE) transA = BLIS_CONJ_TRANSPOSE; + else /*if ( transa == BLIS_CONJ_TRANSPOSE )*/ transA = BLIS_CONJ_NO_TRANSPOSE; + M = M ^ N; + N = M ^ N; + M = M ^ N; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + libblis_igemv_check(transA, M, N, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy ); + float* YY = (float*) bli_obj_buffer( y ); + resid = computediffrv(len, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + libblis_igemv_check(transA, M, N, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy ); + double* YY = (double*) bli_obj_buffer( y ); + resid = computediffrv(len, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + libblis_icgemv_check(transA, M, N, Alpha, A, rsa, + csa, cfa, X, incx, Beta, Y, incy, cfx ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + resid = computediffiv(len, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + libblis_icgemv_check(transA, M, N, Alpha, A, rsa, + csa, cfa, X, incx, Beta, Y, incy, cfx ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + resid = computediffiv(len, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_ger.cpp b/gtestsuite/src/ref_ger.cpp new file mode 100644 index 000000000..1a2824dad --- /dev/null +++ b/gtestsuite/src/ref_ger.cpp @@ -0,0 +1,184 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_ger.h" + +using namespace std; + +//* ========================================================================== +//*> GER performs the rank 1 operation +//*> A := alpha*x*y**T + A, +//*> where alpha is a scalar, x is an m element vector, y is an n element +//*> vector and A is an m by n matrix. +//* ========================================================================== + +template +void libblis_iger_check(dim_t M, dim_t N, T *alpha, T *X, dim_t incx, + T* Y, dim_t incy, T* A, dim_t rsa, dim_t csa) { + T Alpha = alpha[0]; + T temp; + dim_t i, ix, j, jy, kx; + T ZERO = 0.0; + + if ((M == 0) || (N == 0) || + (Alpha == ZERO)) + return; + + if (incy > 0) { + jy = 0; + } + else { + jy = 1 - (N - 1)*incy; + } + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (M - 1)*incx; + } + + for(j = 0; j < N ; j++) { + if (Y[jy] != ZERO) { + temp = Alpha * Y[jy]; + ix = kx; + for(i = 0 ; i < M ; i++) { + A[i*rsa + j *csa] = A[i*rsa + j *csa] + temp * X[ix]; + ix = ix + incx; + } + } + jy = jy + incy; + } + return; +} + +template +void libblis_icger_check(dim_t M, dim_t N, T *alpha, T *X, dim_t incx, + bool conjx, T* Y, dim_t incy, bool conjy, T* A, dim_t rsa, dim_t csa) { + + T Alpha = alpha[0]; + T temp; + dim_t i, ix, j, jy, kx; + T ZERO = {0.0 , 0.0}; + + if ((M == 0) || (N == 0) || + ((Alpha.real == ZERO.real) &&(Alpha.imag == ZERO.imag))) + return; + + ix = 0; + if(conjx) { + for(i = 0 ; i < M ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + jy = 0; + if(conjy) { + for(j = 0; j < N ; j++) { + Y[jy] = conjugate(Y[jy]); + jy = jy + incy; + } + } + + if (incy > 0) { + jy = 0; + } + else { + jy = 1 - (N - 1)*incy; + } + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (M - 1)*incx; + } + + for(j = 0; j < N ; j++) { + if ((Y[jy].real != ZERO.real) || (Y[jy].imag != ZERO.imag)) { + temp = mulc(Alpha , Y[jy]); + ix = kx; + for(i = 0 ; i < M ; i++) { + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , mulc(temp , X[ix])); + ix = ix + incx; + } + } + jy = jy + incy; + } + return; +} + +double libblis_test_iger_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_length( a ); + dim_t N = bli_obj_width( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t incy = bli_obj_vector_inc( y ); + bool conjx = bli_obj_has_conj( x ); + bool conjy = bli_obj_has_conj( y ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a_orig ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y ); + float* AA = (float*) bli_obj_buffer( a ); + libblis_iger_check(M, N, Alpha, X, incx, + Y, incy, A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a_orig ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y ); + double* AA = (double*) bli_obj_buffer( a ); + libblis_iger_check(M, N, Alpha, X, incx, + Y, incy, A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a_orig ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* AA = (scomplex*) bli_obj_buffer( a ); + libblis_icger_check(M, N, Alpha, X, incx, conjx, + Y, incy, conjy, A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a_orig ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* AA = (dcomplex*) bli_obj_buffer( a ); + libblis_icger_check(M, N, Alpha, X, incx, conjx, + Y, incy, conjy, A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_hemm.cpp b/gtestsuite/src/ref_hemm.cpp new file mode 100644 index 000000000..8dbf0aabb --- /dev/null +++ b/gtestsuite/src/ref_hemm.cpp @@ -0,0 +1,486 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_hemm.h" + +using namespace std; + +//* ========================================================================== +//*> HEMM performs one of the matrix-matrix operations +//*> C := alpha*A*B + beta*C, +//*> or +//*> C := alpha*B*A + beta*C, +//*> where alpha and beta are scalars, A is an hermitian matrix and B and +//*> C are m by n matrices. +//* ========================================================================== + +template +void libblis_ihemm_check(side_t side, uplo_t uplo, dim_t M, dim_t N, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T ONE = 1.0; + T ZERO = 0.0; + T tmp1, tmp2; + bool LSIDE, UPPER; + dim_t i, j, k; + + //* Test the input parameters. + LSIDE = (side == BLIS_LEFT); + UPPER = (uplo == BLIS_UPPER); + + if( (M == 0 || N == 0) || ( Alpha == ZERO && Beta == ONE ) ) + return; + + //* And when Alpha.eq.zero. + if( Alpha == ZERO ) + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + return; + } + + //* Start the operations. + if( LSIDE ) + { + //* Form C := Alpha*A*B + Beta*C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp1 = Alpha*B[i*rsb + j*csb]; + tmp2 = ZERO; + for( k = 0 ; k < i ; k++ ) + { + C[k*rsc + j*csc] = C[k*rsc + j*csc] + tmp1*A[k*rsa + i*csa]; + tmp2 = tmp2 + B[k*rsb + j*csb] * A[k*rsa + i*csa]; + } + if (Beta == ZERO) + { + C[i*rsc + j*csc] = tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = (M-1) ; i >= 0 ; i-- ) + { + tmp1 = Alpha*B[i*rsb + j*csb]; + tmp2 = ZERO; + for( k = (i+1) ; k < M ; k++ ) + { + C[k*rsc + j*csc] = C[k*rsc + j*csc] + tmp1*A[k*rsa + i*csa]; + tmp2 = tmp2 + B[k*rsb + j*csb]*A[k*rsa + i*csa]; + } + if (Beta == ZERO) + { + C[i*rsc + j*csc] = tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + } + } + } + } + else + { + //* Form C := Alpha*B*A + Beta*C. + for( j = 0 ; j < N ; j++ ) + { + tmp1 = Alpha*A[j*rsa + j*csa]; + if( Beta == ZERO ) + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = tmp1*B[i*rsb + j*csb]; + } + } + else + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1*B[i*rsb + j*csb]; + } + } + for( k = 0 ; k < j ; k++ ) + { + if( UPPER ) + { + tmp1 = Alpha*A[k*rsa + j*csa]; + } + else + { + tmp1 = Alpha*A[j*rsa + k*csa]; + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp1*B[i*rsb + k*csb]; + } + } + for( k = (j+1) ; k < N ; k++ ) + { + if( UPPER ) + { + tmp1 = Alpha*A[j*rsa + k*csa]; + } + else + { + tmp1 = Alpha*A[k*rsa + j*csa]; + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp1*B[i*rsb + k*csb]; + } + } + } + } + return; +} + +template +void libblis_ichemm_check(side_t side, uplo_t uplo, dim_t M, dim_t N, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T tmp1, tmp2; + bool LSIDE, UPPER; + dim_t i, j, k; + + //* Test the input parameters. + LSIDE = (side == BLIS_LEFT); + UPPER = (uplo == BLIS_UPPER); + + if( (M == 0 || N == 0) || ( Alpha.real == ZERO.real && Beta.real == ONE.real ) ) + return; + + //* And when Alpha.eq.zero. + if( Alpha.real == ZERO.real ) + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + return; + } + + //* Start the operations. + if( LSIDE ) + { + //* Form C := Alpha*A*B + Beta*C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp1 = mulc(Alpha , B[i*rsb + j*csb]); + tmp2 = ZERO; + for( k = 0 ; k < i ; k++ ) + { + C[k*rsc + j*csc] = addc(C[k*rsc + j*csc] , mulc(tmp1 , A[k*rsa + i*csa])); + tmp2 = addc(tmp2 , mulc(B[k*rsb + j*csb] , conjugate(A[k*rsa + i*csa]))); + } + if (Beta.real == ZERO.real) + { + C[i*rsc + j*csc] = addc(mulc(tmp1 , real(A[i*rsa + i*csa])) , mulc(Alpha , tmp2)); + } + else + { + tmp2 = addc(mulc(tmp1 , real(A[i*rsa + i*csa])) , mulc(Alpha , tmp2)); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , tmp2); + //C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]) + mulc(tmp1 , real(A[i*rsa + i*csa])) + mulc(Alpha , tmp2); + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = (M-1) ; i >= 0 ; i-- ) + { + tmp1 = mulc(Alpha , B[i*rsb + j*csb]); + tmp2 = ZERO; + for( k = (i+1) ; k < M ; k++ ) + { + C[k*rsc + j*csc] = addc(C[k*rsc + j*csc] , mulc(tmp1 , A[k*rsa + i*csc])); + tmp2 = addc(tmp2 , mulc(B[k*rsb + j*csb] , conjugate(A[k*rsa + i*csa]))); + } + if (Beta.real == ZERO.real) + { + C[i*rsc + j*csc] = addc(mulc(tmp1 , real(A[i*rsa + i*csa])) , mulc(Alpha , tmp2)); + } + else + { + tmp2 = addc(mulc(tmp1 , real(A[i*rsa + i*csa])) , mulc(Alpha , tmp2)); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , tmp2); + //C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]) + mulc(tmp1 , real(A[i*rsa + i*csa])) + mulc(Alpha , tmp2); + } + } + } + } + } + else + { + //* Form C := Alpha*B*A + Beta*C. + for( j = 0 ; j < N ; j++ ) + { + tmp1 = mulc(Alpha , real(A[j*rsa + j*csa])); + if (Beta.real == ZERO.real) + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = mulc(tmp1 , B[i*rsb + j*csb]); + } + } + else + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , mulc(tmp1 , B[i*rsb + j*csb])); + } + } + for( k = 0 ; k < j ; k++ ) + { + if( UPPER ) + { + tmp1 = mulc(Alpha , A[k*rsa + j*csa]); + } + else + { + tmp1 = mulc(Alpha , conjugate(A[j*rsa + k*csa])); + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp1 , B[i*rsb + k*csb])); + } + } + for( k = (j+1) ; k < N ; k++ ) + { + if( UPPER ) + { + tmp1 = mulc(Alpha , conjugate(A[j*rsa + k*csa])); + } + else + { + tmp1 = mulc(Alpha , A[k*rsa + j*csa]); + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp1 , B[i*rsb + k*csb])); + } + } + } + } + return; +} + +double libblis_test_ihemm_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + dim_t rsb = bli_obj_row_stride( b ) ; + dim_t csb = bli_obj_col_stride( b ) ; + dim_t rsc = bli_obj_row_stride( c ) ; + dim_t csc = bli_obj_col_stride( c ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_ihemm_check(side, uploa, M, N, *Alpha, A, rsa, csa, + B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_ihemm_check(side, uploa, M, N, *Alpha, A, rsa, csa, + B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, N, CC, C, rsc, csc); + } + break; + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + libblis_ichemm_check(side, uploa, M, N, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + libblis_ichemm_check(side, uploa, M, N, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_hemm(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_hemv.cpp b/gtestsuite/src/ref_hemv.cpp new file mode 100644 index 000000000..ed1e829ce --- /dev/null +++ b/gtestsuite/src/ref_hemv.cpp @@ -0,0 +1,305 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_hemv.h" + +using namespace std; + +//* ========================================================================== +//*> HEMV performs the matrix-vector operation +//*> y := alpha*A*x + beta*y +//*> where alpha and beta are scalars, x and y are n element vectors and +//*> A is an n by n hermitian matrix. +//* ========================================================================== + +template +void libblis_ihemv_check(uplo_t uploa, dim_t M, T* alpha, T* A, + dim_t rsa, dim_t csa, T* X, dim_t incx, T* beta, T* Y, dim_t incy) { + T ONE = 1.0; + T ZERO = 0.0; + T Alpha = alpha[0]; + T Beta = beta[0]; + T tmp1, tmp2; + dim_t i, ix, iy, j, jx, jy, kx, ky; + + if ((M == 0) || + ((Alpha == ZERO) && (Beta == ONE))) + return ; + + //* Set up the start points in X and Y. + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (M * incx); + } + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (M * incy); + } + + //* First form y := beta*y. + if (Beta != ONE) { + iy = ky; + if (Beta == ZERO) { + for(i = 0 ; i < M ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < M ; i++) { + Y[iy] = (Beta * Y[iy]); + iy = iy + incy; + } + } + } + + if (Alpha == ZERO) + return; + + T tmp = 0.0 ; + if(uploa == BLIS_UPPER) { + //* Form y when A is stored in upper triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = (Alpha * X[jx]); + tmp2 = ZERO; + ix = kx; + iy = ky; + for(i = 0 ; i < j ; i++) { + tmp = A[i*rsa + j*csa]; + Y[iy] = Y[iy] + (tmp1 * tmp); + tmp2 = tmp2 + (tmp * X[ix]); + ix = ix + incx; + iy = iy + incy; + } + tmp = A[j*rsa + j*csa]; + Y[jy] = Y[jy] + (tmp1 * tmp) + (Alpha * tmp2); + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form y when A is stored in lower triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = (Alpha * X[jx]); + tmp = A[j*rsa + j*csa]; + tmp2 = ZERO; + Y[jy] = Y[jy] + (tmp1 * tmp); + ix = jx; + iy = jy; + for(i = (j+1) ; i < M ; i++) { + ix = ix + incx; + iy = iy + incy; + tmp = A[i*rsa + j*csa]; + Y[iy] = Y[iy] + (tmp1 * tmp); + tmp2 = tmp2 + (tmp * X[ix]); + } + Y[jy] = Y[jy] + (Alpha * tmp2); + jx = jx + incx; + jy = jy + incy; + } + } + + return; +} + +template +void libblis_ichemv_check(uplo_t uploa, dim_t M, T* alpha, T* A, dim_t rsa, +dim_t csa, bool conja, T* X, dim_t incx, bool conjx, T* beta, T* Y, dim_t incy) { + T ONE = { 1.0, 0.0 }; + T ZERO = { 0.0, 0.0 }; + T Alpha = *alpha; + T Beta = *beta; + T tmp1, tmp2; + dim_t i, ix, iy, j, jx, jy, kx, ky; + + if ((M == 0) || + ((Alpha.real == ZERO.real) && (Beta.real == ONE.real))) + return ; + + //* Set up the start points in X and Y. + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (M * incx); + } + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (M * incy); + } + + //* First form y := beta*y. + if((Beta.real != ONE.real) && (Beta.imag != ONE.imag)) { + iy = ky; + if((Beta.real != ZERO.real) && (Beta.imag != ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < M ; i++) { + Y[iy] = mulc(Beta , Y[iy]); + iy = iy + incy; + } + } + } + + if((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) + return; + + if(conjx) { + ix = 0; + for(i = 0 ; i < M ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(conja) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < M ; j++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + T tmp = {0.0, 0.0}; + if(uploa == BLIS_UPPER) { + //* Form y when A is stored in upper triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = mulc(Alpha , X[jx]); + tmp2 = ZERO; + ix = kx; + iy = ky; + for(i = 0 ; i < j ; i++) { + tmp = A[i*rsa + j*csa]; + Y[iy] = addc(Y[iy] , mulc(tmp1 , tmp)); + tmp2 = addc(tmp2 , mulc(conjugate(tmp) , X[ix])); + ix = ix + incx; + iy = iy + incy; + } + tmp = A[j*rsa + j*csa]; + tmp = addc(mulc(tmp1 , real(tmp)) , mulc(Alpha , tmp2)); + Y[jy] = addc(Y[jy] , tmp ); + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form y when A is stored in lower triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = mulc(Alpha , X[jx]); + tmp = A[j*rsa + j*csa]; + tmp2 = ZERO; + Y[jy] = addc(Y[jy] , mulc(tmp1 , real(tmp))); + ix = jx; + iy = jy; + for(i = (j+1) ; i < M ; i++) { + ix = ix + incx; + iy = iy + incy; + tmp = A[i*rsa + j*csa]; + Y[iy] = addc(Y[iy] , mulc(tmp1 , tmp)); + tmp2 = addc(tmp2 , mulc(conjugate(tmp) , X[ix])); + } + Y[jy] = addc(Y[jy] , mulc(Alpha , tmp2)); + jx = jx + incx; + jy = jy + incy; + } + } + + return; +} + +double libblis_test_ihemv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( a ); + dim_t rsa = bli_obj_row_stride( a ); + dim_t csa = bli_obj_col_stride( a ); + bool conja = bli_obj_has_conj( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t incy = bli_obj_vector_inc( y ); + bool conjx = bli_obj_has_conj( x ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_ihemv_check(uploa, M, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_ihemv_check(uploa, M, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_ichemv_check(uploa, M, Alpha, A, rsa, csa, + conja, X, incx, conjx, Beta, Y, incy); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_ichemv_check(uploa, M, Alpha, A, rsa, csa, + conja, X, incx, conjx, Beta, Y, incy); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_her.cpp b/gtestsuite/src/ref_her.cpp new file mode 100644 index 000000000..915f534c4 --- /dev/null +++ b/gtestsuite/src/ref_her.cpp @@ -0,0 +1,203 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_her.h" + +using namespace std; + +//* ========================================================================== +//*> HER performs the hermitian rank 1 operation +//*> A := alpha*x*x**H + A +//*> where alpha is a real scalar, x is an n element vector and A is an +//*> n by n hermitian matrix. +//* ========================================================================== + +template +void libblis_iher_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + T* A, dim_t rsa, dim_t csa) { + T ZERO = 0.0; + T Alpha = alpha[0]; + T temp; + int i, ix, j, jx, kx; + + if((N == 0) || (Alpha == ZERO)) + return; + + /* Set the start point in X if the increment is not unity. */ + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if(uploa == BLIS_UPPER) { + /* Form A when A is stored in upper triangle. */ + jx = kx; + for(j = 0 ; j < N ; j++) { + if (X[jx] != ZERO) { + temp = Alpha * X[jx]; + ix = kx; + for(i = 0 ; i <= j ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + (X[ix] * temp); + ix = ix + incx; + } + } + jx = jx + incx; + } + } + else { + /* Form A when A is stored in lower triangle. */ + jx = kx; + for(j = 0; j < N ; j++) { + if (X[jx] != ZERO) { + temp = Alpha * X[jx]; + ix = jx; + for(i = j ; i < N ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + (X[ix] * temp); + ix = ix + incx; + } + } + jx = jx + incx; + } + } + + return; +} + +template +void libblis_icher_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + bool conjx, T* A, dim_t rsa, dim_t csa) { + T ZERO = {0.0 , 0.0}; + T Alpha = alpha[0]; + T temp; + int i, ix, j, jx, kx; + + if ((N == 0) || ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag))) + return; + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if(conjx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(uploa == BLIS_UPPER) { + /* Form A when A is stored in upper triangle. */ + jx = kx; + for(j = 0 ; j < N ; j++) { + if ((X[jx].real != ZERO.real) || (X[jx].imag != ZERO.imag)) { + temp = mulc(Alpha , conjugate(X[jx])); + ix = kx; + for(i = 0 ; i < j ; i++) { + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , mulc(X[ix] , temp)); + ix = ix + incx; + } + A[j*rsa + j*csa] = real(addc(A[j*rsa + j*csa] , mulc(X[jx] , temp))); + } + else { + A[j*rsa + j*csa] = real(A[j*rsa + j*csa]); + } + jx = jx + incx; + } + } + else { + /* Form A when A is stored in lower triangle. */ + jx = kx; + for(j = 0; j < N ; j++) { + if ((X[jx].real != ZERO.real) || (X[jx].imag != ZERO.imag)) { + temp = mulc(Alpha , conjugate(X[jx])); + A[j*rsa + j*csa] = real(addc(A[j*rsa + j*csa] , mulc(temp , X[jx]))); + ix = jx; + for( i = (j+1) ; i < N ; i++) { + ix = ix + incx; + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , mulc(X[ix] , temp)); + } + } + else { + A[j*rsa + j*csa] = real(A[j*rsa + j*csa]); + } + jx = jx + incx; + } + } + + return; +} + +double libblis_test_iher_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +){ + + num_t dt = bli_obj_dt( x ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( a ); + dim_t N = bli_obj_width( a ); + dim_t incx = bli_obj_vector_inc( x ); + bool conjx = bli_obj_has_conj( x ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a_orig ); + float* X = (float*) bli_obj_buffer( x ); + float* AA = (float*) bli_obj_buffer( a ); + libblis_iher_check(uploa, M, Alpha, X, incx, + A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a_orig ); + double* X = (double*) bli_obj_buffer( x ); + double* AA = (double*) bli_obj_buffer( a ); + libblis_iher_check(uploa, M, Alpha, X, incx, + A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a_orig ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* AA = (scomplex*) bli_obj_buffer( a ); + libblis_icher_check(uploa, M, Alpha, X, incx, conjx, + A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a_orig ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* AA = (dcomplex*) bli_obj_buffer( a ); + libblis_icher_check(uploa, M, Alpha, X, incx, conjx, + A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + diff --git a/gtestsuite/src/ref_her2.cpp b/gtestsuite/src/ref_her2.cpp new file mode 100644 index 000000000..bebc1dc16 --- /dev/null +++ b/gtestsuite/src/ref_her2.cpp @@ -0,0 +1,259 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_her2.h" + +using namespace std; + +//* ========================================================================== +//*> HER2 performs the hermitian rank 2 operation +//*> A := alpha*x*y**H + conjg( alpha )*y*x**H + A, +//*> where alpha is a scalar, x and y are n element vectors and A is an n +//*> by n hermitian matrix. +//* ========================================================================== + +template +void libblis_iher2_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + T* Y, dim_t incy, T* A, dim_t rsa, dim_t csa) { + + T ZERO = 0.0; + T Alpha = alpha[0]; + T tmp1, tmp2; + int i, ix, iy, j, jx, jy, kx, ky; + + if ((N == 0) || (Alpha == ZERO)) + return; + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (N * incy); + } + jx = kx; + jy = ky; + + if(uploa == BLIS_UPPER) { + //* Form A when A is stored in the upper triangle. + for(j = 0 ; j < N ; j++) { + if ((X[jx] != ZERO) || (Y[jy] != ZERO)) { + tmp1 = Alpha * Y[jy]; + tmp2 = Alpha * X[jx]; + ix = kx; + iy = ky; + for(i = 0 ; i < j ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + X[ix]*tmp1 + Y[iy]*tmp2; + ix = ix + incx; + iy = iy + incy; + } + A[j*rsa + j*csa] = A[j*rsa + j*csa] + X[jx]*tmp1 + Y[jy]*tmp2; + } + else { + A[j*rsa + j*csa] = A[j*rsa + j*csa]; + } + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form A when A is stored in the lower triangle. + for(j = 0 ; j < N ; j++) { + if((X[jx] != ZERO) || (Y[jy] != ZERO)) { + tmp1 = Alpha * Y[jy]; + tmp2 = Alpha * X[jx]; + A[j*rsa + j*csa] = A[j*rsa + j*csa] + X[jx]*tmp1 + Y[jy]*tmp2; + ix = jx; + iy = jy; + for(i = (j+1) ;i < N; i++) { + ix = ix + incx; + iy = iy + incy; + A[i*rsa + j*csa] = A[i*rsa + j*csa] + X[ix]*tmp1 + Y[iy]*tmp2; + } + } + else { + A[j*rsa + j*csa] = A[j*rsa + j*csa]; + } + jx = jx + incx; + jy = jy + incy; + } + } + return; +} + +template +void libblis_icher2_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + bool conjx, T* Y, dim_t incy, bool conjy, T* A, dim_t rsa, dim_t csa) { + + T ZERO = {0.0, 0.0}; + T Alpha = *alpha; + T tmp1, tmp2; + int i, ix, iy, j, jx, jy, kx, ky; + + if((N == 0) || ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag))) + return; + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (N * incy); + } + jx = kx; + jy = ky; + + if(conjx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(conjy) { + iy = 0; + for(i = 0 ; i < N ; i++) { + Y[iy] = conjugate(Y[iy]); + iy = iy + incy; + } + } + + T p1, p2, p; + if(uploa == BLIS_UPPER) { + //* Form A when A is stored in the upper triangle. + for(j = 0 ; j < N ; j++) { + tmp1 = mulc(Alpha , conjugate(Y[jy])); + tmp2 = conjugate(mulc(Alpha , X[jx])); + ix = kx; + iy = ky; + for(i = 0 ; i < j ; i++) { + p1 = mulc(X[ix] , tmp1); + p2 = mulc(Y[iy] , tmp2); + p = addc(p1 , p2); + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , p); + ix = ix + incx; + iy = iy + incy; + } + p1 = mulc(X[jx] , tmp1); + p2 = mulc(Y[jy] , tmp2); + p = addc(p1 , p2); + A[j*rsa + j*csa] = real(addc(A[j*rsa + j*csa] , p)); + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form A when A is stored in the lower triangle. + for(j = 0; j < N ; j++) { + tmp1 = mulc(Alpha , conjugate(Y[jy])); + tmp2 = conjugate(mulc(Alpha , X[jx])); + p1 = mulc(X[jx] , tmp1); + p2 = mulc(Y[jy] , tmp2); + p = addc(p1 , p2); + A[j*rsa + j*csa] = real(addc(A[j*rsa + j*csa] , p)); + ix = jx; + iy = jy; + for(i = (j+1) ;i < N; i++) { + ix = ix + incx; + iy = iy + incy; + p1 = mulc(X[ix] , tmp1); + p2 = mulc(Y[iy] , tmp2); + p = addc(p1 , p2); + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , p); + } + jx = jx + incx; + jy = jy + incy; + } + } + return; +} + +double libblis_test_iher2_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + + num_t dt = bli_obj_dt( x ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( a ); + dim_t N = bli_obj_width( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t incy = bli_obj_vector_inc( y ); + bool conjx = bli_obj_has_conj( x ); + bool conjy = bli_obj_has_conj( y ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a_orig ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y ); + float* AA = (float*) bli_obj_buffer( a ); + libblis_iher2_check(uploa, M, Alpha, X, incx, + Y, incy, A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a_orig ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y ); + double* AA = (double*) bli_obj_buffer( a ); + libblis_iher2_check(uploa, M, Alpha, X, incx, + Y, incy, A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a_orig ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* AA = (scomplex*) bli_obj_buffer( a ); + libblis_icher2_check(uploa, M, Alpha, X, incx, conjx, + Y, incy, conjy, A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a_orig ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* AA = (dcomplex*) bli_obj_buffer( a ); + libblis_icher2_check(uploa, M, Alpha, X, incx, conjx, + Y, incy, conjy, A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + + diff --git a/gtestsuite/src/ref_her2k.cpp b/gtestsuite/src/ref_her2k.cpp new file mode 100644 index 000000000..6cc13fdc4 --- /dev/null +++ b/gtestsuite/src/ref_her2k.cpp @@ -0,0 +1,666 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_her2k.h" + +using namespace std; + +//*============================================================================ +//*> HER2K performs one of the hermitian rank 2k operations +//*> C := alpha*A*B**H + conjg( alpha )*B*A**H + beta*C, +//*> or +//*> C := alpha*A**H*B + conjg( alpha )*B**H*A + beta*C, +//*============================================================================ + +template +void libblis_iher2k_check( uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T tmp1, tmp2; + int i, j, l; + bool UPPER, NOTRANS; + + T ONE = 1.0 ; + T ZERO = 0.0 ; + + //* Test the input parameters. + UPPER = ( uplo == BLIS_UPPER ); + NOTRANS = ( trans == BLIS_NO_TRANSPOSE ); + + if( N == 0 || (( Alpha == ZERO || K == 0 ) && Beta == ONE )) + return; + + //* And when alpha.eq.zero. + if( Alpha == ZERO ) + { + if( UPPER ) + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + else + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + return; + } + + //* Start the operations. + if( NOTRANS ) + { + //* C := alpha*A*B**T + alpha*B*A**T + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta == ZERO ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta != ONE ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + for( l = 0 ; l < K ; l++ ) + { + if( (A[j*rsa + l*csa] != ZERO) || (B[j*rsb + l*csb] != ZERO) ) + { + tmp1 = Alpha*B[j*rsb + l*csb]; + tmp2 = Alpha*A[j*rsa + l*csa]; + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + A[i*rsa + l*csa]*tmp1 + B[i*rsb + l*csb]*tmp2; + } + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta == ZERO ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta != ONE ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + for( l = 0 ; l < K ; l++ ) + { + if( (A[j*rsa + l*csa] != ZERO) || (B[j*rsb + l*csb] != ZERO) ) + { + tmp1 = Alpha*B[j*rsb + l*csb]; + tmp2 = Alpha*A[j*rsa + l*csa]; + for( i = j; i < N ; i++ ) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + A[i*rsa + l*csa]*tmp1 + B[i*rsb + l*csb]*tmp2; + } + } + } + } + } + } + else + { + //* C := alpha*A**T*B + alpha*B**T*A + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = tmp1 + A[l*rsa + i*csa]*B[l*rsb + j*csb]; + tmp2 = tmp2 + B[l*rsb + i*csb]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp1 + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp1 + Alpha*tmp2; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = tmp1 + A[l*rsa + i*csa]*B[l*rsb + j*csb]; + tmp2 = tmp2 + B[l*rsb + i*csb]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp1 + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp1 + Alpha*tmp2; + } + } + } + } + } + return; +} + +template +void libblis_icher2k_check( uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T tmp1, tmp2; + T tmpa, tmpb; + int i, j, l; + bool UPPER, NOTRANS; + + T ONE = { 1.0 , 0.0 }; + T ZERO = { 0.0 , 0.0 }; + + //* Test the input parameters. + UPPER = (uplo == BLIS_UPPER); + NOTRANS = (trans == BLIS_NO_TRANSPOSE); + + if( N == 0 || (( Alpha.real == ZERO.real || K == 0 ) && Beta.real == ONE.real )) + return; + + //* And when alpha.eq.zero. + if( Alpha.real == ZERO.real ) + { + if( UPPER ) + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < j ; i++) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + } + } + } + else + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + for( i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + } + return; + } + + //* Start the operations. + if( NOTRANS ) + { + //* Form C := alpha*A*B**H + conjg( alpha )*B*A**H + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta.real == ZERO.real ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta.real != ONE.real ) + { + for(i = 0 ; i < j ; i++) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + } + else + { + C[j*rsc + j*csc] = real(C[j*rsc + j*csc]); + } + for( l = 0 ; l < K ; l++ ) + { + if( ((A[j*rsa + l*csa].real != ZERO.real) || (A[j*rsa + l*csa].imag != ZERO.imag)) + || ((B[j*rsb + l*csb].real != ZERO.real) || (B[j*rsb + l*csb].imag != ZERO.imag)) ) + { + tmp1 = mulc(Alpha , conjugate(B[j*rsb + l*csb])); + tmp2 = conjugate(mulc(Alpha , A[j*rsa + l*csa])); + for( i = 0 ; i < j ; i++) + { + tmpa = mulc(A[i*rsa + l*csa] , tmp1); + tmpb = mulc(B[i*rsb + l*csb] , tmp2); + tmpa = addc(tmpa , tmpb); + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , tmpa); + } + tmpa = mulc(A[j*rsa + l*csa] , tmp1); + tmpb = mulc(B[j*rsb + l*csb] , tmp2); + tmpa = addc(tmpa , tmpb); + C[j*rsc + j*csc] = addc(real(C[j*rsc + j*csc]) , real(tmpa)); + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta.real == ZERO.real ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta.real != ONE.real ) + { + for( i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + } + else + { + C[j*rsc + j*csc] = real(C[j*rsc + j*csc]); + } + for( l = 0 ; l < K ; l++ ) + { + if( ((A[j*rsa + l*csa].real != ZERO.real) || (A[j*rsa + l*csa].imag != ZERO.imag)) + || ((B[j*rsb + l*csb].real != ZERO.real) || (B[j*rsb + l*csb].imag != ZERO.imag)) ) + { + tmp1 = mulc(Alpha , conjugate(B[j*rsb + l*csb])); + tmp2 = conjugate(mulc(Alpha , A[j*rsa + l*csa])); + for( i = (j+1) ; i < N ; i++ ) + { + tmpa = mulc(A[i*rsa + l*csa] , tmp1); + tmpb = mulc(B[i*rsb + l*csb] , tmp2); + tmpa = addc(tmpa, tmpb); + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , tmpa); + } + tmpa = mulc(A[j*rsa + l*csa] , tmp1); + tmpb = mulc(B[j*rsb + l*csb] , tmp2); + tmpa = addc(tmpa, tmpb); + C[j*rsc + j*csc] = addc(real(C[j*rsc + j*csc]) , real(tmpa)); + } + } + } + } + } + else + { + //* Form C := alpha*A**H*B + conjg( alpha )*B**H*A + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = addc(tmp1 , mulc(conjugate(A[l*rsa + i*csa]) , B[l*rsb + j*csb])); + tmp2 = addc(tmp2 , mulc(conjugate(B[l*rsb + i*csb]) , A[l*rsa + j*csa])); + } + if( i == j ) + { + if( Beta.real == ZERO.real ) + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(conjugate(Alpha) , tmp2); + C[j*rsc + j*csc] = real(addc(tmpa, tmpb)); + } + else + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(conjugate(Alpha) , tmp2); + tmpa = addc(tmpa, tmpb); + C[j*rsc + j*csc] = addc(mulc(Beta , real(C[j*rsc + j*csc])) , real(tmpa)); + } + } + else + { + if( Beta.real == ZERO.real ) + { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp1) , mulc(conjugate(Alpha) ,tmp2)); + } + else + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(conjugate(Alpha) , tmp2); + tmpa = addc(tmpa , tmpb); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) ,tmpa); + } + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = addc(tmp1 , mulc(conjugate(A[l*rsa + i*csa]) , B[l*rsb + j*csb])); + tmp2 = addc(tmp2 , mulc(conjugate(B[l*rsb + i*csb]) , A[l*rsa + j*csa])); + } + if( i == j ) + { + if( Beta.real == ZERO.real ) + { + C[j*rsc + j*csc] = real(addc(mulc(Alpha , tmp1) , mulc(conjugate(Alpha) , tmp2))); + } + else + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(conjugate(Alpha) , tmp2); + tmpa = addc(tmpa, tmpb); + C[j*rsc + j*csc] = addc(mulc(Beta , real(C[j*rsc + j*csc])) , real(tmpa)); + } + } + else + { + if( Beta.real == ZERO.real ) + { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp1) , mulc(conjugate(Alpha) , tmp2)); + } + else + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(conjugate(Alpha) , tmp2); + tmpa = addc(tmpa, tmpb); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , tmpa); + } + } + } + } + } + } + return; +} + +double libblis_test_iher2k_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + num_t dt = bli_obj_dt( c ); + uplo_t uploc = bli_obj_uplo( c ); + dim_t M = bli_obj_length( c ); + dim_t K = bli_obj_width_after_trans( a ); + trans_t trans = bli_obj_onlytrans_status( a ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + dim_t rsb = bli_obj_row_stride( b ) ; + dim_t csb = bli_obj_col_stride( b ) ; + dim_t rsc = bli_obj_row_stride( c ) ; + dim_t csc = bli_obj_col_stride( c ) ; + double resid = 0.0; + f77_int lda, ldb, ldc; + + if( bli_obj_is_col_stored( c ) ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + int nrowa; + if (trans == BLIS_NO_TRANSPOSE) { + nrowa = M; + } else { + nrowa = K; + } + + if( lda < max(1, nrowa) ) { + return resid; + } + if( ldb < max(1, nrowa) ) { + return resid; + } + if( ldc < max(1, (int)M) ) { + return resid; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_iher2k_check(uploc, trans, M, K, *Alpha, A, + rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_iher2k_check(uploc, trans, M, K, *Alpha, A, + rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, M, CC, C, rsc, csc); + } + break; + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + Beta->imag = 0.0 ; + libblis_icher2k_check(uploc, trans, M, K, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + Beta->imag = 0.0 ; + libblis_icher2k_check(uploc, trans, M, K, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return abs(resid); +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_her2k(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_herk.cpp b/gtestsuite/src/ref_herk.cpp new file mode 100644 index 000000000..4c6f8c4be --- /dev/null +++ b/gtestsuite/src/ref_herk.cpp @@ -0,0 +1,648 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_herk.h" + +using namespace std; + +//* ========================================================================== +//*> HERK performs one of the hermitian rank k operations +//*> C := alpha*A*A**H + beta*C, +//*> or +//*> C := alpha*A**H*A + beta*C, +//*> where alpha and beta are real scalars, C is an n by n hermitian +//*> matrix and A is an n by k matrix in the first case and a k by n +//*> matrix in the second case. +//* ========================================================================== + +template +void libblis_iherk_check( uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T Alpha, T* A, dim_t rsa, dim_t csa, T Beta, T* C, dim_t rsc, dim_t csc ) +{ + T tmp, rtmp; + dim_t i, j, l; + bool UPPER, NOTRANS; + + T ONE = 1.0; + T ZERO = 0.0; + + UPPER = (uplo == BLIS_UPPER); + NOTRANS = (trans == BLIS_NO_TRANSPOSE) || (trans == BLIS_CONJ_NO_TRANSPOSE); + + if( (N == 0) || (( Alpha == ZERO || K == 0) && Beta == ONE ) ) + return; + + //* And when alpha.eq.zero. + if( Alpha == ZERO ) + { + if( UPPER ) + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < j ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + C[j*rsc + j*csc] = Beta*(C[j*rsc + j*csc]); + } + } + } + else + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + C[j*rsc + j*csc] = Beta*C[j*rsc + j*csc]; + for( i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + return; + } + + //* Start the operations. + if( NOTRANS ) + { + //* Form C := alpha*A*A**H + beta*C. + if( UPPER ) + { + for( j = 0; j < N ; j++ ) + { + if( Beta == ZERO ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta != ONE ) + { + for(i = 0 ; i < j ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + C[j*rsc + j*csc] = Beta*C[j*rsc + j*csc]; + } + for( l = 0 ; l < K ; l++ ) + { + if( A[j*rsa + l*csa] != ZERO ) + { + tmp = Alpha*A[j*rsa + l*csa] ; + for( i = 0 ; i < j ; i++ ) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp*A[i*rsa + l*csa]; + } + C[j*rsc + j*csc] = C[j*rsc + j*csc] + tmp*A[i*rsa + l*csa]; + } + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + if( Beta == ZERO ) + { + for( i = j ; i < N; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta != ONE ) + { + C[j*rsc + j*csc] = Beta*C[j*rsc + j*csc]; + for(i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + for( l = 0 ; l < K ; l++ ) + { + if( A[j*rsa + l*csa] != ZERO ) + { + tmp = Alpha*A[j*rsa + l*csa]; + C[j*rsc + j*csc] = C[j*rsc + j*csc] + tmp*A[j*rsa + l*csa]; + for( i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp*A[i*rsa + l*csa]; + } + } + } + } + } + } + else + { + //* Form C := alpha*A**H*A + beta*C. + if( UPPER ) + { + for( j = 0; j < N ; j++ ) + { + for( i = 0 ; i < j ; i++ ) + { + tmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp = tmp + A[l*rsa + i*csa]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp; + } + else + { + C[i*rsc + j*csc] = Alpha*tmp + Beta*C[i*rsc + j*csc]; + } + } + rtmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + rtmp = rtmp + A[l*rsa + j*csa]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[j*rsc + j*csc] = Alpha*rtmp; + } + else + { + C[j*rsc + j*csc] = Alpha*rtmp + Beta*C[j*rsc + j*csc]; + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + rtmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + rtmp = rtmp + A[l*rsa + j*csa]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[j*rsc + j*csc] = Alpha*rtmp; + } + else + { + C[j*rsc + j*csc] = Alpha*rtmp + Beta*C[j*rsc + j*csc]; + } + for( i = (j+1) ; i < N ; i++ ) + { + tmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp = tmp + A[l*rsa + i*csa]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp; + } + else + { + C[i*rsc + j*csc] = Alpha*tmp + Beta*C[i*rsc + j*csc]; + } + } + } + } + } + return; +} + +template +void libblis_icherk_check(uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T Alpha,T* A, dim_t rsa, dim_t csa, T Beta, T* C, dim_t rsc, dim_t csc) +{ + T tmp; + T rtmp; + dim_t i, j, l; + bool UPPER, NOTRANS; + T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + + UPPER = (uplo == BLIS_UPPER); + NOTRANS = (trans == BLIS_NO_TRANSPOSE) || (trans == BLIS_CONJ_NO_TRANSPOSE); + + //* Quick return if possible. + if( (N == 0) || + (((Alpha.real == ZERO.real) || (K == 0)) && (Beta.real == ONE.real)) ) + { + return; + } + +//* And when alpha.eq.zero. + if( Alpha.real == ZERO.real ) + { + if( UPPER ) + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0; i < j ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + } + } + } + else + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + for( i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + } + return; + } + + //* Start the operations. + if( NOTRANS ) + { + //*Form C := alpha*A*A**H + beta*C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta.real == ZERO.real ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta.real != ONE.real ) + { + for( i = 0 ; i < j ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + C[j*rsc + j*csc] = mulc(Beta , real(C[j*rsc + j*csc])); + } + else + { + C[j*rsc + j*csc] = real(C[j*rsc + j*csc]); + } + + for( l = 0; l < K ; l++ ) + { + if((A[j*rsa + l*csa].real != ZERO.real) || (A[j*rsa + l*csa].imag != ZERO.imag)) + { + tmp = mulc(Alpha , conjugate(A[j*rsa + l*csa])); + for( i = 0 ; i < j ; i++ ) + { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp , A[i*rsa + l*csa])); + } + C[j*rsc + j*csc] = addc(real(C[j*rsc + j*csc]) , real(mulc(tmp ,A[i*rsa + l*csa]))); + } + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + if( Beta.real == ZERO.real ) + { + for( i = j ; i < N ; i++ ) + { + C[j*rsc + j*csc] = ZERO; + } + } + else if( Beta.real != ONE.real ) + { + C[j*rsc + j*csc] = mulc(Beta ,real(C[j*rsc + j*csc])); + for( i = (j+1) ; i < N ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + else + { + C[j*rsc + j*csc] = real(C[j*rsc + j*csc]); + } + + for( l = 0; l < K ; l++ ) + { + if( (A[j*rsa + l*csa].real != ZERO.real)||(A[j*rsa + l*csa].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , conjugate(A[j*rsa + l*csa])); + C[j*rsc + j*csc] = addc(real(C[j*rsc + j*csc]) , real(mulc(tmp , A[j*rsa + l*csa]))); + for( i = (j+1) ; i < N; i++ ) + { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp , A[i*rsa + l*csa])); + } + } + } + } + } + } + else + { + //* Form C := alpha*A**H*A + beta*C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < j ; i++ ) + { + tmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp = addc(tmp , mulc(conjugate(A[l*rsa + i*csa]) , A[l*rsa + j*csa])); + } + if( Beta.real == ZERO.real ) + { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else + { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp) , mulc(Beta , C[i*rsc + j*csc])); + } + } + rtmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + rtmp = addc(rtmp , mulc(conjugate(A[l*rsa + j*csa]) , A[l*rsa + j*csa])); + } + if( Beta.real == ZERO.real ) + { + C[j*rsc + j*csc] = mulc(Alpha , rtmp); + } + else + { + C[j*rsc + j*csc] = addc(mulc(Alpha , rtmp) , mulc(Beta , real(C[j*rsc + j*csc]))); + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + rtmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + rtmp = addc(rtmp , mulc(conjugate(A[l*rsa + j*csa]) , A[l*rsa + j*csa])); + } + if( Beta.real == ZERO.real ) + { + C[j*rsc + j*csc] = mulc(Alpha , rtmp); + } + else + { + C[j*rsc + j*csc] = addc(mulc(Alpha , rtmp) , mulc(Beta , real(C[j*rsc + j*csc]))); + } + for( i = (j+1) ; i < N ; i++ ) + { + tmp = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp = addc(tmp , mulc(conjugate(A[l*rsa + i*csa]) , A[l*rsa + j*csa])); + } + if( Beta.real == ZERO.real ) + { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else + { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp) , mulc(Beta , C[i*rsc + j*csc])); + } + } + } + } + } + return; +} + +double libblis_test_iherk_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig +){ + num_t dt = bli_obj_dt( a ); + dim_t M = bli_obj_length( c ); + dim_t K = bli_obj_width_after_trans( a ); + uplo_t uplo = bli_obj_uplo( c ); + trans_t trans = bli_obj_onlytrans_status( a ); + double resid = 0.0; + dim_t rsa, csa; + dim_t rsc, csc; + f77_int lda; + + rsa = bli_obj_row_stride( a ) ; + csa = bli_obj_col_stride( a ) ; + rsc = bli_obj_row_stride( c ) ; + csc = bli_obj_col_stride( c ) ; + + if( bli_obj_is_col_stored( c ) ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + int nrowa; + if (trans == BLIS_NO_TRANSPOSE) { + nrowa = M; + } else { + nrowa = K; + } + + if (lda < max(1, nrowa)) { + return resid; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_iherk_check( uplo, trans, M, K, *Alpha, + A, rsa, csa, *Beta, C, rsc, csc ); + resid = computediffrm(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_iherk_check(uplo, trans, M, K, *Alpha, + A, rsa, csa, *Beta, C, rsc, csc ); + resid = computediffrm(M, M, CC, C, rsc, csc); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + Alpha->imag = 0.0 ; + Beta->imag = 0.0 ; + libblis_icherk_check(uplo, trans, M, K, *Alpha, + A, rsa, csa, *Beta, C, rsc, csc); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + Alpha->imag = 0.0 ; + Beta->imag = 0.0 ; + libblis_icherk_check(uplo, trans, M, K, *Alpha, + A, rsa, csa, *Beta, C, rsc, csc); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + +template +double libblis_check_nan_real( dim_t rsc, dim_t csc, obj_t* c ) { + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + double resid = 0.0; + T* C = (T*) bli_obj_buffer( c ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rsc, dim_t csc, obj_t* c ) { + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + double resid = 0.0; + U* C = (U*) bli_obj_buffer( c ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_herk(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_is_col_stored( c ) ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_normfm.cpp b/gtestsuite/src/ref_normfm.cpp new file mode 100644 index 000000000..945c3504d --- /dev/null +++ b/gtestsuite/src/ref_normfm.cpp @@ -0,0 +1,109 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_normfm.h" + +using namespace std; + +//* ========================================================================== +//*> NORMFM performs matrix operation +//*> Compute the Frobenius norm (bli_?normfm()) +//*> of the elements in an m x n matrix A. The resulting norm is stored to norm +//* ========================================================================== + +template +T libblis_inormfm_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx ) { + + dim_t i, j; + T sum = 0.0; + T norm = 0.0; + + if ((M == 0) || (N == 0)) { + return norm; + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + sum += X[i*rsx + j*csx] * X[i*rsx + j*csx]; + } + } + + norm = sqrt( abs(sum) ); + + return norm; +} + +template +U libblis_icnormfm_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx ) { + + dim_t i, j; + T rr = { 0.0, 0.0 }; + U norm = 0.0; + + if ((M == 0) || (N == 0)) { + return norm; + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + auto a = X[i*rsx + j*csx]; + rr.real += a.real * a.real; + rr.imag += a.imag * a.imag; + } + } + + U r = rr.real + rr.imag; + norm = sqrt( abs(r) ); + + return norm; +} + +double libblis_test_inormfm_check( + test_params_t* params, + obj_t* x, + obj_t* norm +){ + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_length( x ); + dim_t N = bli_obj_width( x ); + dim_t rsx = bli_obj_row_stride( x ) ; + dim_t csx = bli_obj_col_stride( x ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* av = (float*) bli_obj_internal_scalar_buffer( norm ); + float rv = libblis_inormfm_check(M, N, X, rsx, csx); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* av = (double*) bli_obj_internal_scalar_buffer( norm ); + double rv = libblis_inormfm_check(M, N, X, rsx, csx); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + float* av = (float*) bli_obj_internal_scalar_buffer( norm ); + float rv = libblis_icnormfm_check(M, N, X, rsx, csx); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + double* av = (double*) bli_obj_internal_scalar_buffer( norm ); + double rv = libblis_icnormfm_check(M, N, X, rsx, csx); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return abs(resid); +} diff --git a/gtestsuite/src/ref_normfv.cpp b/gtestsuite/src/ref_normfv.cpp new file mode 100644 index 000000000..03f9d2db4 --- /dev/null +++ b/gtestsuite/src/ref_normfv.cpp @@ -0,0 +1,107 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_normfv.h" + +using namespace std; + +//* ========================================================================== +//*> NORMFV performs vector operations +//*> Compute the Frobenius norm (bli_?normfv()) +//*> of the elements in a vector x of length n. The resulting norm is stored to norm +//* ========================================================================== + +template +T libblis_inormfv_check(dim_t len, T* X, dim_t incx ) { + dim_t i, ix; + T sum = 0.0; + T norm = 0.0; + + if (len == 0){ + return norm; + } + + ix = 0; + for(i = 0 ; i < len ; i++) { + sum += X[ix] * X[ix]; + ix = ix + incx; + } + + norm = sqrt( abs(sum) ); + + return norm; +} + +template +U libblis_icnormfv_check(dim_t len, T* X, dim_t incx ) { + dim_t i, ix; + T rr = { 0.0, 0.0 }; + U norm = 0.0; + if(len == 0) { + return norm; + } + + ix = 0; + for(i = 0 ; i < len ; i++) { + //rr = addc(rr, mulc(X[ix] , X[ix])); + auto a = X[ix]; + rr.real += a.real * a.real; + rr.imag += a.imag * a.imag; + ix = ix + incx; + } + + U r = rr.real + rr.imag; + norm = sqrt( abs(r) ); + + return norm; +} + +double libblis_test_inormfv_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* n +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* av = (float*) bli_obj_internal_scalar_buffer( n ); + float rv = libblis_inormfv_check(M, X, incx ); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* av = (double*) bli_obj_internal_scalar_buffer( n ); + double rv = libblis_inormfv_check(M, X, incx ); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + float* av = (float*) bli_obj_internal_scalar_buffer( n ); + float rv = libblis_icnormfv_check(M, X, incx ); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + double* av = (double*) bli_obj_internal_scalar_buffer( n ); + double rv = libblis_icnormfv_check(M, X, incx ); + resid = (double)(abs(rv - *av)/abs(rv)); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} diff --git a/gtestsuite/src/ref_scal2m.cpp b/gtestsuite/src/ref_scal2m.cpp new file mode 100644 index 000000000..a78d45c9a --- /dev/null +++ b/gtestsuite/src/ref_scal2m.cpp @@ -0,0 +1,163 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scal2m.h" + +using namespace std; + +//* ========================================================================== +//*> SCAL2M performs matrix operations +//*> B := alpha * transa(A) +//*> where A is an m x n matrix, and alpha is a scalar. +//* ========================================================================== + +template +void libblis_iscal2m_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, T* Y, dim_t rsy, dim_t csy ) { + + dim_t i, j; + T ONE = 1.0 ; + T ZERO = 0.0 ; + T Alpha = alpha[0]; + + if ((M == 0) || (N == 0)) { + return; + } + + if (Alpha != ONE) { + if (Alpha == ZERO) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = ZERO; + } + } + } + else { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = Alpha * X[i*rsx + j*csx]; + } + } + } + } + + return; +} + +template +void libblis_icscal2m_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, bool conjx, T* Y, dim_t rsy, dim_t csy) { + dim_t i, j; + T ONE = {1.0, 0.0} ; + T ZERO = {0.0, 0.0} ; + T Alpha = *alpha; + + if ((M == 0) || (N == 0)) { + return; + } + + if(conjx) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = conjugate(X[i*rsx + j*csx]); + } + } + } + + /* First form x := Alpha*x. */ + if ((Alpha.real != ONE.real) && (Alpha.imag != ONE.imag)) { + if ((Alpha.real != ZERO.real) && (Alpha.imag != ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy]= ZERO; + } + } + } + else { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = mulc(Alpha , X[i*rsx + j*csx]); + } + } + } + } + + return; +} + +double libblis_test_iscal2m_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + dim_t M = bli_obj_length( y ); + dim_t N = bli_obj_width( y ); + bool transx = bli_obj_has_trans( x ); + bool conjx = bli_obj_has_conj( x ); + dim_t rsy = bli_obj_row_stride( y ) ; + dim_t csy = bli_obj_col_stride( y ) ; + dim_t rsx, csx; + double resid = 0.0; + + if( bli_obj_is_col_stored( x ) ) { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } else { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iscal2m_check(M, N, Alpha, X, rsx, csx, + Y, rsy, csy); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iscal2m_check(M, N, Alpha, X, rsx, csx, + Y, rsy, csy); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icscal2m_check(M, N, Alpha, X, rsx, csx, + conjx, Y, rsy, csy); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icscal2m_check(M, N, Alpha, X, rsx, csx, + conjx, Y, rsy, csy); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_scal2v.cpp b/gtestsuite/src/ref_scal2v.cpp new file mode 100644 index 000000000..8d1806b31 --- /dev/null +++ b/gtestsuite/src/ref_scal2v.cpp @@ -0,0 +1,146 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scal2v.h" + +using namespace std; + +//* ========================================================================== +//*> SCAL2V performs vector operations +//*> y := alpha * conjx(x) +//*> where x is a vector of length n, and alpha is a scalar. +//* ========================================================================== + +template +void libblis_iscal2v_check(dim_t len, T* alpha, T* X, dim_t incx, + T* Y, dim_t incy) { + dim_t i, ix, iy; + //T ONE = 1.0 ; + T ZERO = 0.0 ; + T Alpha = alpha[0]; + + if (len == 0){ + return; + } + + ix = 0; + iy = 0; + if (Alpha == ZERO) { + for(i = 0 ; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = Alpha * X[ix]; + iy = iy + incy; + ix = ix + incx; + } + } + + return; +} + +template +void libblis_icscal2v_check(dim_t len, T* alpha, T* X, dim_t incx, + T* Y, dim_t incy, bool cfx) { + dim_t i, ix, iy; + //T ONE = {1.0 , 0.0} ; + T ZERO = {0.0 , 0.0} ; + T Alpha = *alpha; + + if(len == 0) { + return; + } + + ix = 0; + if(cfx) { + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + ix = 0; + iy = 0; + if ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) { + for(i = 0; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = mulc(Alpha , X[ix]); + ix = ix + incx; + iy = iy + incy; + } + } + + return; +} + +double libblis_test_iscal2v_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + bool cfx = bli_obj_has_conj( x ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_iscal2v_check(M, Alpha, X, incx, Y, incy ); + resid = computediffrv(M, incx, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_iscal2v_check(M, Alpha, X, incx, Y, incy ); + resid = computediffrv(M, incx, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icscal2v_check(M, Alpha, X, incx, + Y, incy, cfx ); + resid = computediffiv(M, incx, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icscal2v_check(M, Alpha, X, incx, + Y, incy, cfx ); + resid = computediffiv(M, incx, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_scalm.cpp b/gtestsuite/src/ref_scalm.cpp new file mode 100644 index 000000000..cc9f8f3c5 --- /dev/null +++ b/gtestsuite/src/ref_scalm.cpp @@ -0,0 +1,139 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scalm.h" + +using namespace std; + +//* ========================================================================== +//*> SCALM performs matrix operations +//*> A := conjalpha(alpha) * A +//*> where A is an m x n matrix, and alpha is a scalar. +//* ========================================================================== + +template +void libblis_iscalm_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx ) { + + dim_t i, j; + T ONE = 1.0 ; + T ZERO = 0.0 ; + T Alpha = alpha[0]; + + if ((M == 0) || (N == 0)) { + return; + } + + if (Alpha != ONE) { + if (Alpha == ZERO) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = ZERO; + } + } + } + else { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = Alpha * X[i*rsx + j*csx]; + } + } + } + } + + return; +} + +template +void libblis_icscalm_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, bool cfalpha) { + dim_t i, j; + T ONE = {1.0, 0.0} ; + T ZERO = {0.0, 0.0} ; + T Alpha = *alpha; + + if ((M == 0) || (N == 0)) { + return; + } + + if(cfalpha) + Alpha = conjugate(Alpha); + + /* First form x := Alpha*x. */ + if ((Alpha.real != ONE.real) && (Alpha.imag != ONE.imag)) { + if ((Alpha.real != ZERO.real) && (Alpha.imag != ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = ZERO; + } + } + } + else { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = mulc(Alpha , X[i*rsx + j*csx]); + } + } + } + } + + return; +} + +double libblis_test_iscalm_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* x_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_length( x ); + dim_t N = bli_obj_width( x ); + dim_t rsx = bli_obj_row_stride( x ) ; + dim_t csx = bli_obj_col_stride( x ) ; + bool cfalpha = bli_obj_has_conj( alpha ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x_orig ); + float* XX = (float*) bli_obj_buffer( x ); + libblis_iscalm_check(M, N, Alpha, X, rsx, csx); + resid = computediffrm(M, N, XX, X, rsx, csx); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x_orig ); + double* XX = (double*) bli_obj_buffer( x ); + libblis_iscalm_check(M, N, Alpha, X, rsx, csx); + resid = computediffrm(M, N, XX, X, rsx, csx); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x_orig ); + scomplex* XX = (scomplex*) bli_obj_buffer( x ); + libblis_icscalm_check(M, N, Alpha, X, rsx, csx, cfalpha); + resid = computediffim(M, N, XX, X, rsx, csx); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x_orig ); + dcomplex* XX = (dcomplex*) bli_obj_buffer( x ); + libblis_icscalm_check(M, N, Alpha, X, rsx, csx, cfalpha); + resid = computediffim(M, N, XX, X, rsx, csx); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_scalv.cpp b/gtestsuite/src/ref_scalv.cpp new file mode 100644 index 000000000..2af4e2d8c --- /dev/null +++ b/gtestsuite/src/ref_scalv.cpp @@ -0,0 +1,130 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scalv.h" + +using namespace std; + +//* ========================================================================== +//*> SCALV performs vector operations +//*> x := conjalpha(alpha) * x +//*> where x is a vector of length n, and alpha is a scalar. +//* ========================================================================== + +template +void libblis_iscalv_check(dim_t len, T* beta, T* X, dim_t incx) { + + dim_t i, ix; + T ONE = 1.0 ; + T ZERO = 0.0 ; + T Beta = beta[0]; + + if (len == 0){ + return; + } + + if( Beta != ONE ) { + ix = 0; + if (Beta == ZERO) { + for( i = 0 ; i < len ; i++ ) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for( i = 0 ; i < len ; i++ ) { + X[ix] = Beta * X[ix]; + ix = ix + incx; + } + } + } + return; +} + +template +void libblis_icscalv_check(dim_t len, T* beta, T* X, dim_t incx, bool cfbeta) { + dim_t i, ix; + T ONE = {1.0, 0.0} ; + T ZERO = {0.0, 0.0} ; + T Beta = *beta; + + if( len == 0 ) { + return; + } + + if( cfbeta ) + Beta = conjugate(Beta); + + /* First form x := beta*x. */ + if( Beta.real != ONE.real ) { + ix = 0; + if( (Beta.real != ZERO.real) && (Beta.imag != ZERO.imag) ) { + for(i = 0; i < len ; i++) { + X[ix] = ZERO; + ix = ix + incx; + } + } + else { + for( i = 0 ; i < len ; i++ ) { + X[ix] = mulc(Beta , X[ix]); + ix = ix + incx; + } + } + } + return; +} + +double libblis_test_iscalv_check( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* x_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + bool cfbeta = bli_obj_has_conj( beta ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Beta = (float*) bli_obj_buffer( beta ); + float* X = (float*) bli_obj_buffer( x_orig ); + float* XX = (float*) bli_obj_buffer( x ); + libblis_iscalv_check(M, Beta, X, incx ); + resid = computediffrv(M, incx, XX, X); + break; + } + case BLIS_DOUBLE : + { + double* Beta = (double*) bli_obj_buffer( beta ); + double* X = (double*) bli_obj_buffer( x_orig ); + double* XX = (double*) bli_obj_buffer( x ); + libblis_iscalv_check(M, Beta, X, incx ); + resid = computediffrv(M, incx, XX, X); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* X = (scomplex*) bli_obj_buffer( x_orig ); + scomplex* XX = (scomplex*) bli_obj_buffer( x ); + libblis_icscalv_check(M, Beta, X, incx, cfbeta ); + resid = computediffiv(M, incx, XX, X); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x_orig ); + dcomplex* XX = (dcomplex*) bli_obj_buffer( x ); + libblis_icscalv_check(M, Beta, X, incx, cfbeta ); + resid = computediffiv(M, incx, XX, X); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} diff --git a/gtestsuite/src/ref_subm.cpp b/gtestsuite/src/ref_subm.cpp new file mode 100644 index 000000000..8916a81eb --- /dev/null +++ b/gtestsuite/src/ref_subm.cpp @@ -0,0 +1,130 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_subm.h" + +using namespace std; + +//* ========================================================================== +//*> SUBM performs matrix operations +//*> B := B - transa(A) +//*> where B is an m x n matrix. +//* ========================================================================== + +template +void libblis_isubm_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx, + T* Y, dim_t rsy, dim_t csy, T* YY) { + + dim_t i, j; + + if ((M == 0) || (N == 0)) { + return; + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = Y[i*rsy + j*csy] - X[i*rsx + j*csx] ; + } + } + + return; +} + +template +void libblis_icsubm_check(dim_t M, dim_t N, T* X, dim_t rsx, dim_t csx, + conj_t conjx, T* Y, dim_t rsy, dim_t csy) { + + dim_t i, j; + + if ((M == 0) || (N == 0)) { + return; + } + + if(conjx) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = conjugate(X[i*rsx + j*csx]); + } + } + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = subc(Y[i*rsy + j*csy], X[i*rsx + j*csx]); + } + } + + return; +} + +double libblis_test_isubm_check( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + bool transx = bli_obj_has_trans( x ); + conj_t conjx = bli_obj_conj_status( x ); + dim_t M = bli_obj_length( y ); + dim_t N = bli_obj_width( y ); + dim_t rsy = bli_obj_row_stride( y ) ; + dim_t csy = bli_obj_col_stride( y ) ; + double resid = 0.0; + dim_t rsx, csx; + + if( bli_obj_is_col_stored( x ) ) { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } else { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_isubm_check( M, N, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_isubm_check( M, N, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icsubm_check( M, N, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icsubm_check( M, N, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_subv.cpp b/gtestsuite/src/ref_subv.cpp new file mode 100644 index 000000000..0137a8fe7 --- /dev/null +++ b/gtestsuite/src/ref_subv.cpp @@ -0,0 +1,117 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_subv.h" + +using namespace std; + +//* ========================================================================== +//*> SUBV performs vector operations +//*> y := y - conjx(x) +//*> where x and y are vectors of length n. +//* ========================================================================== + +template +void libblis_isubv_check(dim_t len, T* X, dim_t incx, T* Y, dim_t incy) { + + dim_t i, ix, iy; + if (len == 0) { + return; + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = Y[iy] - X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +template +void libblis_icsubv_check(dim_t len, T* X, dim_t incx, T* Y, + dim_t incy, bool cfx) { + dim_t i, ix, iy; + if (len == 0) { + return; + } + + ix = 0; + if(cfx) { + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = subc(Y[iy] , X[ix]); + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +double libblis_test_isubv_check( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_isubv_check( M, X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_isubv_check( M, X, incx, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icsubv_check( M, X, incx, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icsubv_check( M, X, incx, Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_symm.cpp b/gtestsuite/src/ref_symm.cpp new file mode 100644 index 000000000..177413ed5 --- /dev/null +++ b/gtestsuite/src/ref_symm.cpp @@ -0,0 +1,486 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_symm.h" + +using namespace std; + +//* ========================================================================== +//*> SYMM performs one of the matrix-matrix operations +//*> C := alpha*A*B + beta*C, +//*> or +//*> C := alpha*B*A + beta*C, +//*> where alpha and beta are scalars, A is a symmetric matrix and B and +//*> C are m by n matrices. +//* ========================================================================== + +template +void libblis_isymm_check(side_t side, uplo_t uplo, dim_t M, dim_t N, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T ONE = 1.0; + T ZERO = 0.0; + T tmp1, tmp2; + bool LSIDE, UPPER; + dim_t i, j, k; + + //* Test the input parameters. + LSIDE = (side == BLIS_LEFT); + UPPER = (uplo == BLIS_UPPER); + + if( (M == 0 || N == 0) || ( Alpha == ZERO && Beta == ONE ) ) + return; + + //* And when Alpha.eq.zero. + if( Alpha == ZERO ) + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + return; + } + + //* Start the operations. + if( LSIDE ) + { + //* Form C := Alpha*A*B + Beta*C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp1 = Alpha*B[i*rsb + j*csb]; + tmp2 = ZERO; + for( k = 0 ; k < i ; k++ ) + { + C[k*rsc + j*csc] = C[k*rsc + j*csc] + tmp1*A[k*rsa + i*csa]; + tmp2 = tmp2 + B[k*rsb + j*csb] * A[k*rsa + i*csa]; + } + if (Beta == ZERO) + { + C[i*rsc + j*csc] = tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = (M-1) ; i >= 0 ; i-- ) + { + tmp1 = Alpha*B[i*rsb + j*csb]; + tmp2 = ZERO; + for( k = (i+1) ; k < M ; k++ ) + { + C[k*rsc + j*csc] = C[k*rsc + j*csc] + tmp1*A[k*rsa + i*csa]; + tmp2 = tmp2 + B[k*rsb + j*csb]*A[k*rsa + i*csa]; + } + if (Beta == ZERO) + { + C[i*rsc + j*csc] = tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1*A[i*rsa + i*csa] + Alpha*tmp2; + } + } + } + } + } + else + { + //* Form C := Alpha*B*A + Beta*C. + for( j = 0 ; j < N ; j++ ) + { + tmp1 = Alpha*A[j*rsa + j*csa]; + if( Beta == ZERO ) + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = tmp1*B[i*rsb + j*csb]; + } + } + else + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1*B[i*rsb + j*csb]; + } + } + for( k = 0 ; k < j ; k++ ) + { + if( UPPER ) + { + tmp1 = Alpha*A[k*rsa + j*csa]; + } + else + { + tmp1 = Alpha*A[j*rsa + k*csa]; + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp1*B[i*rsb + k*csb]; + } + } + for( k = (j+1) ; k < N ; k++ ) + { + if( UPPER ) + { + tmp1 = Alpha*A[j*rsa + k*csa]; + } + else + { + tmp1 = Alpha*A[k*rsa + j*csa]; + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp1*B[i*rsb + k*csb]; + } + } + } + } + return; +} + +template +void libblis_icsymm_check(side_t side, uplo_t uplo, dim_t M, dim_t N, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T tmp1, tmp2; + bool LSIDE, UPPER; + dim_t i, j, k; + + //* Test the input parameters. + LSIDE = (side == BLIS_LEFT); + UPPER = (uplo == BLIS_UPPER); + + if( (M == 0 || N == 0) || ( Alpha.real == ZERO.real && Beta.real == ONE.real ) ) + return; + + //* And when Alpha.eq.zero. + if( Alpha.real == ZERO.real ) + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + return; + } + + //* Start the operations. + if( LSIDE ) + { + //* Form C := Alpha*A*B + Beta*C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp1 = mulc(Alpha , B[i*rsb + j*csb]); + tmp2 = ZERO; + for( k = 0 ; k < i ; k++ ) + { + C[k*rsc + j*csc] = addc(C[k*rsc + j*csc] , mulc(tmp1 , A[k*rsa + i*csa])); + tmp2 = addc(tmp2 , mulc(B[k*rsb + j*csb] , A[k*rsa + i*csa])); + } + if (Beta.real == ZERO.real) + { + C[i*rsc + j*csc] = addc(mulc(tmp1 , A[i*rsa + i*csa]) , mulc(Alpha , tmp2)); + } + else + { + tmp2 = addc(mulc(tmp1 , A[i*rsa + i*csa]) , mulc(Alpha , tmp2)); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , tmp2); + //C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1 * A[i*rsa + i*csa] + Alpha*tmp2; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = (M-1) ; i >= 0 ; i-- ) + { + tmp1 = mulc(Alpha , B[i*rsb + j*csb]); + tmp2 = ZERO; + for( k = (i+1) ; k < M ; k++ ) + { + C[k*rsc + j*csc] = addc(C[k*rsc + j*csc] , mulc(tmp1 , A[k*rsa + i*csc])); + tmp2 = addc(tmp2 , mulc(B[k*rsb + j*csb] , A[k*rsa + i*csa])); + } + if (Beta.real == ZERO.real) + { + C[i*rsc + j*csc] = addc(mulc(tmp1 , A[i*rsa + i*csa]) , mulc(Alpha , tmp2)); + } + else + { + tmp2 = addc(mulc(tmp1 , A[i*rsa + i*csa]) , mulc(Alpha , tmp2)); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , tmp2); + //C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + tmp1 * A[i*rsa + i*csa] + Alpha*tmp2; + } + } + } + } + } + else + { + //* Form C := Alpha*B*A + Beta*C. + for( j = 0 ; j < N ; j++ ) + { + tmp1 = mulc(Alpha , A[j*rsa + j*csa]); + if (Beta.real == ZERO.real) + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = mulc(tmp1 , B[i*rsb + j*csb]); + } + } + else + { + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , mulc(tmp1 , B[i*rsb + j*csb])); + } + } + for( k = 0 ; k < j ; k++ ) + { + if( UPPER ) + { + tmp1 = mulc(Alpha , A[k*rsa + j*csa]); + } + else + { + tmp1 = mulc(Alpha , A[j*rsa + k*csa]); + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp1 , B[i*rsb + k*csb])); + } + } + for( k = (j+1) ; k < N ; k++ ) + { + if( UPPER ) + { + tmp1 = mulc(Alpha , A[j*rsa + k*csa]); + } + else + { + tmp1 = mulc(Alpha , A[k*rsa + j*csa]); + } + for(i = 0 ; i < M ; i++) + { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp1 , B[i*rsb + k*csb])); + } + } + } + } + return; +} + +double libblis_test_isymm_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + dim_t rsb = bli_obj_row_stride( b ) ; + dim_t csb = bli_obj_col_stride( b ) ; + dim_t rsc = bli_obj_row_stride( c ) ; + dim_t csc = bli_obj_col_stride( c ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_isymm_check(side, uploa, M, N, *Alpha, A, rsa, csa, + B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_isymm_check(side, uploa, M, N, *Alpha, A, rsa, csa, + B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, N, CC, C, rsc, csc); + } + break; + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + libblis_icsymm_check(side, uploa, M, N, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + libblis_icsymm_check(side, uploa, M, N, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_symm(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_symv.cpp b/gtestsuite/src/ref_symv.cpp new file mode 100644 index 000000000..2b2e1acad --- /dev/null +++ b/gtestsuite/src/ref_symv.cpp @@ -0,0 +1,305 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_symv.h" + +using namespace std; + +//* ========================================================================== +//*> SYMV performs the matrix-vector operation +//*> y := alpha*A*x + beta*y +//*> where alpha and beta are scalars, x and y are n element vectors and +//*> A is an n by n symmetric matrix. +//* ========================================================================== + +template +void libblis_isymv_check(uplo_t uploa, dim_t M, T* alpha, T* A, + dim_t rsa, dim_t csa, T* X, dim_t incx, T* beta, T* Y, dim_t incy) { + T ONE = 1.0; + T ZERO = 0.0; + T Alpha = alpha[0]; + T Beta = beta[0]; + T tmp1, tmp2; + dim_t i, ix, iy, j, jx, jy, kx, ky; + + if ((M == 0) || + ((Alpha == ZERO) && (Beta == ONE))) + return ; + + //* Set up the start points in X and Y. + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (M * incx); + } + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (M * incy); + } + + //* First form y := beta*y. + if (Beta != ONE) { + iy = ky; + if (Beta == ZERO) { + for(i = 0 ; i < M ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < M ; i++) { + Y[iy] = (Beta * Y[iy]); + iy = iy + incy; + } + } + } + + if (Alpha == ZERO) + return; + + T tmp = 0.0 ; + if(uploa == BLIS_UPPER) { + //* Form y when A is stored in upper triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = (Alpha * X[jx]); + tmp2 = ZERO; + ix = kx; + iy = ky; + for(i = 0 ; i < j ; i++) { + tmp = A[i*rsa + j*csa]; + Y[iy] = Y[iy] + (tmp1 * tmp); + tmp2 = tmp2 + (tmp * X[ix]); + ix = ix + incx; + iy = iy + incy; + } + tmp = A[j*rsa + j*csa]; + Y[jy] = Y[jy] + (tmp1 * tmp) + (Alpha * tmp2); + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form y when A is stored in lower triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = (Alpha * X[jx]); + tmp = A[j*rsa + j*csa]; + tmp2 = ZERO; + Y[jy] = Y[jy] + (tmp1 * tmp); + ix = jx; + iy = jy; + for(i = (j+1) ; i < M ; i++) { + ix = ix + incx; + iy = iy + incy; + tmp = A[i*rsa + j*csa]; + Y[iy] = Y[iy] + (tmp1 * tmp); + tmp2 = tmp2 + (tmp * X[ix]); + } + Y[jy] = Y[jy] + (Alpha * tmp2); + jx = jx + incx; + jy = jy + incy; + } + } + + return; +} + +template +void libblis_icsymv_check(uplo_t uploa, dim_t M, T* alpha, T* A, dim_t rsa, +dim_t csa, bool conja, T* X, dim_t incx, bool conjx, T* beta, T* Y, dim_t incy) { + T ONE = { 1.0, 0.0 }; + T ZERO = { 0.0, 0.0 }; + T Alpha = *alpha; + T Beta = *beta; + T tmp1, tmp2; + dim_t i, ix, iy, j, jx, jy, kx, ky; + + if ((M == 0) || + ((Alpha.real == ZERO.real) && (Beta.real == ONE.real))) + return ; + + //* Set up the start points in X and Y. + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (M * incx); + } + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (M * incy); + } + + //* First form y := beta*y. + if((Beta.real != ONE.real) && (Beta.imag != ONE.imag)) { + iy = ky; + if((Beta.real != ZERO.real) && (Beta.imag != ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < M ; i++) { + Y[iy] = mulc(Beta , Y[iy]); + iy = iy + incy; + } + } + } + + if((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) + return; + + if(conjx) { + ix = 0; + for(i = 0 ; i < M ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(conja) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < M ; j++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + T tmp = {0.0, 0.0}; + if(uploa == BLIS_UPPER) { + //* Form y when A is stored in upper triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = mulc(Alpha , X[jx]); + tmp2 = ZERO; + ix = kx; + iy = ky; + for(i = 0 ; i < j ; i++) { + tmp = A[i*rsa + j*csa]; + Y[iy] = addc(Y[iy] , mulc(tmp1 , tmp)); + tmp2 = addc(tmp2 , mulc(tmp , X[ix])); + ix = ix + incx; + iy = iy + incy; + } + tmp = A[j*rsa + j*csa]; + tmp = addc(mulc(tmp1 , tmp) , mulc(Alpha , tmp2)); + Y[jy] = addc(Y[jy] , tmp ); + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form y when A is stored in lower triangle. + jx = kx; + jy = ky; + for(j = 0 ; j < M ; j++) { + tmp1 = mulc(Alpha , X[jx]); + tmp = A[j*rsa + j*csa]; + tmp2 = ZERO; + Y[jy] = addc(Y[jy] , mulc(tmp1 , tmp)); + ix = jx; + iy = jy; + for(i = (j+1) ; i < M ; i++) { + ix = ix + incx; + iy = iy + incy; + tmp = A[i*rsa + j*csa]; + Y[iy] = addc(Y[iy] , mulc(tmp1 , tmp)); + tmp2 = addc(tmp2 , mulc(tmp , X[ix])); + } + Y[jy] = addc(Y[jy] , mulc(Alpha , tmp2)); + jx = jx + incx; + jy = jy + incy; + } + } + + return; +} + +double libblis_test_isymv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( a ); + dim_t rsa = bli_obj_row_stride( a ); + dim_t csa = bli_obj_col_stride( a ); + bool conja = bli_obj_has_conj( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t incy = bli_obj_vector_inc( y ); + bool conjx = bli_obj_has_conj( x ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_isymv_check(uploa, M, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_isymv_check(uploa, M, Alpha, A, rsa, csa, + X, incx, Beta, Y, incy); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icsymv_check(uploa, M, Alpha, A, rsa, csa, + conja, X, incx, conjx, Beta, Y, incy); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icsymv_check(uploa, M, Alpha, A, rsa, csa, + conja, X, incx, conjx, Beta, Y, incy); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_syr.cpp b/gtestsuite/src/ref_syr.cpp new file mode 100644 index 000000000..1f29a5659 --- /dev/null +++ b/gtestsuite/src/ref_syr.cpp @@ -0,0 +1,195 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syr.h" + +using namespace std; + +//* ========================================================================== +//*> SYR performs the symmetric rank 1 operation +//*> A := alpha*x*x**T + A, +//*> where alpha is a real scalar, x is an n element vector and A is an +//*> n by n symmetric matrix. +//* ========================================================================== + +template +void libblis_isyr_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + T* A, dim_t rsa, dim_t csa) { + T ZERO = 0.0; + T Alpha = alpha[0]; + T temp; + int i, ix, j, jx, kx; + + if((N == 0) || (Alpha == ZERO)) + return; + + /* Set the start point in X if the increment is not unity. */ + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if(uploa == BLIS_UPPER) { + /* Form A when A is stored in upper triangle. */ + jx = kx; + for(j = 0; j < N ; j++) { + if (X[jx] != ZERO) { + temp = Alpha*X[jx]; + ix = kx; + for(i = 0 ; i <= j ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + X[ix]*temp; + ix = ix + incx; + } + } + jx = jx + incx; + } + } + else + { + /* Form A when A is stored in lower triangle. */ + jx = kx; + for(j = 0; j < N ; j++) { + if (X[jx] != ZERO) { + temp = Alpha*X[jx]; + ix = jx; + for(i = j ; i < N ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + X[ix]*temp; + ix = ix + incx; + } + } + jx = jx + incx; + } + } + return; +} + +template +void libblis_icsyr_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + bool conjx, T* A, dim_t rsa, dim_t csa) { + T ZERO = {0.0 , 0.0}; + T Alpha = alpha[0]; + T temp; + int i, ix, j, jx, kx; + + if ((N == 0) || ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag))) + return; + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if(conjx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(uploa == BLIS_UPPER) { + /* Form A when A is stored in upper triangle. */ + jx = kx; + for(j = 0 ; j < N ; j++) { + if ((X[jx].real != ZERO.real) || (X[jx].imag != ZERO.imag)) { + temp = mulc(Alpha , X[jx]); + ix = kx; + for(i = 0 ; i <= j ; i++) { + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , mulc(X[ix] , temp)); + ix = ix + incx; + } + } + jx = jx + incx; + } + } + else { + /* Form A when A is stored in lower triangle. */ + jx = kx; + for(j = 0; j < N ; j++) { + if ((X[jx].real != ZERO.real) || (X[jx].imag != ZERO.imag)) { + temp = mulc(Alpha , X[jx]); + ix = jx; + for(i = j ; i < N ; i++) { + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , mulc(X[ix] , temp)); + ix = ix + incx; + } + } + jx = jx + incx; + } + } + + return; +} + +double libblis_test_isyr_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +){ + + num_t dt = bli_obj_dt( x ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( a ); + dim_t N = bli_obj_width( a ); + dim_t incx = bli_obj_vector_inc( x ); + bool conjx = bli_obj_has_conj( x ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a_orig ); + float* X = (float*) bli_obj_buffer( x ); + float* AA = (float*) bli_obj_buffer( a ); + libblis_isyr_check(uploa, M, Alpha, X, incx, + A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a_orig ); + double* X = (double*) bli_obj_buffer( x ); + double* AA = (double*) bli_obj_buffer( a ); + libblis_isyr_check(uploa, M, Alpha, X, incx, + A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a_orig ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* AA = (scomplex*) bli_obj_buffer( a ); + libblis_icsyr_check(uploa, M, Alpha, X, incx, conjx, + A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a_orig ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* AA = (dcomplex*) bli_obj_buffer( a ); + libblis_icsyr_check(uploa, M, Alpha, X, incx, conjx, + A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + diff --git a/gtestsuite/src/ref_syr2.cpp b/gtestsuite/src/ref_syr2.cpp new file mode 100644 index 000000000..c0a923e7b --- /dev/null +++ b/gtestsuite/src/ref_syr2.cpp @@ -0,0 +1,243 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syr2.h" + +using namespace std; + +//* ========================================================================== +//*> SYR2 performs the symmetric rank 2 operation +//*> A := alpha*x*y**T + alpha*y*x**T + A, +//*> where alpha is a scalar, x and y are n element vectors and A is an n +//*> by n symmetric matrix. +//* ========================================================================== + +template +void libblis_isyr2_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + T* Y, dim_t incy, T* A, dim_t rsa, dim_t csa) { + + T ZERO = 0.0; + T Alpha = alpha[0]; + T tmp1, tmp2; + int i, ix, iy, j, jx, jy, kx, ky; + + if ((N == 0) || (Alpha == ZERO)) + return; + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (N * incy); + } + jx = kx; + jy = ky; + + if(uploa == BLIS_UPPER) { + //* Form A when A is stored in the upper triangle. + for(j = 0 ; j < N ; j++) { + if ((X[jx] != ZERO) || (Y[jy] != ZERO)) { + tmp1 = Alpha * Y[jy]; + tmp2 = Alpha * X[jx]; + ix = kx; + iy = ky; + for(i = 0 ; i <= j ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + (X[ix] * tmp1) + (Y[iy] * tmp2); + ix = ix + incx; + iy = iy + incy; + } + } + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form A when A is stored in the lower triangle. + for(j = 0 ; j < N ; j++) { + if((X[jx] != ZERO) || (Y[jy] != ZERO)) { + tmp1 = Alpha * Y[jy]; + tmp2 = Alpha * X[jx]; + ix = jx; + iy = jy; + for(i = j ; i < N ; i++) { + A[i*rsa + j*csa] = A[i*rsa + j*csa] + (X[ix] * tmp1) + (Y[iy] * tmp2); + ix = ix + incx; + iy = iy + incy; + } + } + jx = jx + incx; + jy = jy + incy; + } + } + return; +} + +template +void libblis_icsyr2_check(uplo_t uploa, dim_t N, T* alpha, T* X, dim_t incx, + bool conjx, T* Y, dim_t incy, bool conjy, T* A, dim_t rsa, dim_t csa) { + + T ZERO = {0.0, 0.0}; + T Alpha = *alpha; + T tmp1, tmp2; + int i, ix, iy, j, jx, jy, kx, ky; + + if((N == 0) || ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag))) + return; + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if (incy > 0) { + ky = 0; + } + else { + ky = 1 - (N * incy); + } + jx = kx; + jy = ky; + + if(conjx) { + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + if(conjy) { + iy = 0; + for(i = 0 ; i < N ; i++) { + Y[iy] = conjugate(Y[iy]); + iy = iy + incy; + } + } + + T p1, p2, p; + if(uploa == BLIS_UPPER) { + //* Form A when A is stored in the upper triangle. + for(j = 0 ; j < N ; j++) { + tmp1 = mulc(Alpha , Y[jy]); + tmp2 = mulc(Alpha , X[jx]); + ix = kx; + iy = ky; + for(i = 0 ; i <= j ; i++) { + p1 = mulc(X[ix] , tmp1); + p2 = mulc(Y[iy] , tmp2); + p = addc(p1 , p2); + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , p); + ix = ix + incx; + iy = iy + incy; + } + jx = jx + incx; + jy = jy + incy; + } + } + else { + //* Form A when A is stored in the lower triangle. + for(j = 0 ; j < N ; j++) { + tmp1 = mulc(Alpha , Y[jy]); + tmp2 = mulc(Alpha , X[jx]); + ix = jx; + iy = jy; + for(i = j ; i < N ; i++) { + p1 = mulc(X[ix] , tmp1); + p2 = mulc(Y[iy] , tmp2); + p = addc(p1 , p2); + A[i*rsa + j*csa] = addc(A[i*rsa + j*csa] , p); + ix = ix + incx; + iy = iy + incy; + } + jx = jx + incx; + jy = jy + incy; + } + } + return; +} + +double libblis_test_isyr2_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + + num_t dt = bli_obj_dt( x ); + uplo_t uploa = bli_obj_uplo( a ); + dim_t M = bli_obj_length( a ); + dim_t N = bli_obj_width( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t incy = bli_obj_vector_inc( y ); + bool conjx = bli_obj_has_conj( x ); + bool conjy = bli_obj_has_conj( y ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a_orig ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y ); + float* AA = (float*) bli_obj_buffer( a ); + libblis_isyr2_check(uploa, M, Alpha, X, incx, + Y, incy, A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a_orig ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y ); + double* AA = (double*) bli_obj_buffer( a ); + libblis_isyr2_check(uploa, M, Alpha, X, incx, + Y, incy, A, rsa, csa); + resid = computediffrm(M, N, AA, A, rsa, csa); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a_orig ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y ); + scomplex* AA = (scomplex*) bli_obj_buffer( a ); + libblis_icsyr2_check(uploa, M, Alpha, X, incx, conjx, + Y, incy, conjy, A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a_orig ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y ); + dcomplex* AA = (dcomplex*) bli_obj_buffer( a ); + libblis_icsyr2_check(uploa, M, Alpha, X, incx, conjx, + Y, incy, conjy, A, rsa, csa); + resid = computediffim(M, N, AA, A, rsa, csa); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + + diff --git a/gtestsuite/src/ref_syr2k.cpp b/gtestsuite/src/ref_syr2k.cpp new file mode 100644 index 000000000..586399f8b --- /dev/null +++ b/gtestsuite/src/ref_syr2k.cpp @@ -0,0 +1,611 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syr2k.h" + +using namespace std; + +//* ========================================================================== +//*> SYR2K performs one of the symmetric rank 2k operations +//*> C := alpha*A*B**T + alpha*B*A**T + beta*C, +//*> or +//*> C := alpha*A**T*B + alpha*B**T*A + beta*C, +//*> where alpha and beta are scalars, C is an n by n symmetric matrix +//*> and A and B are n by k matrices in the first case and k by n +//*> matrices in the second case. +//* ========================================================================== + +template +void libblis_isyr2k_check( uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T tmp1, tmp2; + int i, j, l; + bool UPPER, NOTRANS; + + T ONE = 1.0 ; + T ZERO = 0.0 ; + + //* Test the input parameters. + UPPER = ( uplo == BLIS_UPPER ); + NOTRANS = ( trans == BLIS_NO_TRANSPOSE ); + + if( N == 0 || (( Alpha == ZERO || K == 0 ) && Beta == ONE )) + return; + + //* And when alpha.eq.zero. + if( Alpha == ZERO ) + { + if( UPPER ) + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + else + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + return; + } + + //* Start the operations. + if( NOTRANS ) + { + //* C := alpha*A*B**T + alpha*B*A**T + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta == ZERO ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta != ONE ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + for( l = 0 ; l < K ; l++ ) + { + if( (A[j*rsa + l*csa] != ZERO) || (B[j*rsb + l*csb] != ZERO) ) + { + tmp1 = Alpha*B[j*rsb + l*csb]; + tmp2 = Alpha*A[j*rsa + l*csa]; + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + A[i*rsa + l*csa]*tmp1 + B[i*rsb + l*csb]*tmp2; + } + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta == ZERO ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( Beta != ONE ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + for( l = 0 ; l < K ; l++ ) + { + if( (A[j*rsa + l*csa] != ZERO) || (B[j*rsb + l*csb] != ZERO) ) + { + tmp1 = Alpha*B[j*rsb + l*csb]; + tmp2 = Alpha*A[j*rsa + l*csa]; + for( i = j; i < N ; i++ ) + { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + A[i*rsa + l*csa]*tmp1 + B[i*rsb + l*csb]*tmp2; + } + } + } + } + } + } + else + { + //* C := alpha*A**T*B + alpha*B**T*A + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = tmp1 + A[l*rsa + i*csa]*B[l*rsb + j*csb]; + tmp2 = tmp2 + B[l*rsb + i*csb]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp1 + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp1 + Alpha*tmp2; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = tmp1 + A[l*rsa + i*csa]*B[l*rsb + j*csb]; + tmp2 = tmp2 + B[l*rsb + i*csb]*A[l*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp1 + Alpha*tmp2; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp1 + Alpha*tmp2; + } + } + } + } + } + return; +} + +template +void libblis_icsyr2k_check( uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T Alpha, T* A, dim_t rsa, dim_t csa, T* B, dim_t rsb, dim_t csb, T Beta, + T* C, dim_t rsc, dim_t csc ) +{ + T tmp1, tmp2; + T tmpa, tmpb; + int i, j, l; + bool UPPER, NOTRANS; + + T ONE = { 1.0 , 0.0 }; + T ZERO = { 0.0 , 0.0 }; + + //* Test the input parameters. + UPPER = (uplo == BLIS_UPPER); + NOTRANS = (trans == BLIS_NO_TRANSPOSE); + + if( N == 0 || (( Alpha.real == ZERO.real || K == 0 ) && Beta.real == ONE.real )) + return; + + //* And when alpha.eq.zero. + if( Alpha.real == ZERO.real ) + { + if( UPPER ) + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + } + else + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + } + return; + } + + //* Start the operations. + if( NOTRANS ) + { + //* Form C := alpha*A*B**H + conjg( alpha )*B*A**H + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + if( Beta.real == ZERO.real ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( (Beta.real != ONE.real) || (Beta.imag != ONE.imag) ) + { + for( i = 0 ; i <= j ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + for( l = 0 ; l < K ; l++ ) + { + if( ((A[j*rsa + l*csa].real != ZERO.real) || (A[j*rsa + l*csa].imag != ZERO.imag)) + || ((B[j*rsb + l*csb].real != ZERO.real) || (B[j*rsb + l*csb].imag != ZERO.imag)) ) + { + tmp1 = mulc(Alpha , B[j*rsb + l*csb]); + tmp2 = mulc(Alpha , A[j*rsa + l*csa]); + for( i = 0 ; i <= j ; i++) + { + tmpa = mulc(A[i*rsa + l*csa] , tmp1); + tmpb = mulc(B[i*rsb + l*csb] , tmp2); + tmpa = addc(tmpa , tmpb); + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , tmpa); + } + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + else if( (Beta.real != ONE.real) || (Beta.imag != ONE.imag) ) + { + for( i = j ; i < N ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + for( l = 0 ; l < K ; l++ ) + { + if( ((A[j*rsa + l*csa].real != ZERO.real) || (A[j*rsa + l*csa].imag != ZERO.imag)) + || ((B[j*rsb + l*csb].real != ZERO.real) || (B[j*rsb + l*csb].imag != ZERO.imag)) ) + { + tmp1 = mulc(Alpha , B[j*rsb + l*csb]); + tmp2 = mulc(Alpha , A[j*rsa + l*csa]); + for( i = j ; i < N ; i++ ) + { + tmpa = mulc(A[i*rsa + l*csa] , tmp1); + tmpb = mulc(B[i*rsb + l*csb] , tmp2); + tmpa = addc(tmpa, tmpb); + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , tmpa); + } + } + } + } + } + } + else + { + //* Form C := alpha*A**T*B + alpha*B**T*A + C. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i <= j ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = addc(tmp1 , mulc(A[l*rsa + i*csa] , B[l*rsb + j*csb])); + tmp2 = addc(tmp2 , mulc(B[l*rsb + i*csb] , A[l*rsa + j*csa])); + } + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp1) , mulc(Alpha ,tmp2)); + } + else + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(Alpha , tmp2); + tmpa = addc(tmpa , tmpb); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) ,tmpa); + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = j ; i < N ; i++ ) + { + tmp1 = ZERO; + tmp2 = ZERO; + for( l = 0 ; l < K ; l++ ) + { + tmp1 = addc(tmp1 , mulc(A[l*rsa + i*csa] , B[l*rsb + j*csb])); + tmp2 = addc(tmp2 , mulc(B[l*rsb + i*csb] , A[l*rsa + j*csa])); + } + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp1) , mulc(Alpha , tmp2)); + } + else + { + tmpa = mulc(Alpha , tmp1); + tmpb = mulc(Alpha , tmp2); + tmpa = addc(tmpa, tmpb); + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , tmpa); + } + } + } + } + } + return; +} + +double libblis_test_isyr2k_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + num_t dt = bli_obj_dt( c ); + uplo_t uploc = bli_obj_uplo( c ); + dim_t M = bli_obj_length( c ); + dim_t K = bli_obj_width_after_trans( a ); + trans_t trans = bli_obj_onlytrans_status( a ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + dim_t rsb = bli_obj_row_stride( b ) ; + dim_t csb = bli_obj_col_stride( b ) ; + dim_t rsc = bli_obj_row_stride( c ) ; + dim_t csc = bli_obj_col_stride( c ) ; + double resid = 0.0; + f77_int lda, ldb, ldc; + + if( bli_obj_is_col_stored( c ) ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + int nrowa; + if (trans == BLIS_NO_TRANSPOSE) { + nrowa = M; + } else { + nrowa = K; + } + + if( lda < max(1, nrowa) ) { + return resid; + } + if( ldb < max(1, nrowa) ) { + return resid; + } + if( ldc < max(1, (int)M) ) { + return resid; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_isyr2k_check(uploc, trans, M, K, *Alpha, A, + rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_isyr2k_check(uploc, trans, M, K, *Alpha, A, + rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, M, CC, C, rsc, csc); + } + break; + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + libblis_icsyr2k_check(uploc, trans, M, K, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + libblis_icsyr2k_check(uploc, trans, M, K, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return abs(resid); +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_syr2k(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_syrk.cpp b/gtestsuite/src/ref_syrk.cpp new file mode 100644 index 000000000..b4d806bcf --- /dev/null +++ b/gtestsuite/src/ref_syrk.cpp @@ -0,0 +1,503 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syrk.h" + +using namespace std; + +//* ========================================================================== +//*> C := alpha*A*A**T + beta*C, +//*> or +//*> C := alpha*A**T*A + beta*C, +//* ========================================================================== + +template +void libblis_isyrk_check(uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T* alpha, T* A, dim_t rsa, dim_t csa, T* beta, T* C, dim_t rsc, dim_t csc) { + + //* .. Local Scalars .. + T tmp; + dim_t i, j, l; + bool UPPER, NOTRANS; + T Alpha = alpha[0]; + T Beta = beta[0]; + + //* .. Parameters .. + T ONE, ZERO; + ONE = 1.0 ; + ZERO = 0.0 ; + + UPPER = (uplo == BLIS_UPPER); + NOTRANS = (trans == BLIS_NO_TRANSPOSE) || (trans == BLIS_CONJ_NO_TRANSPOSE); + + //* Quick return if possible. + if((N == 0) || + (((Alpha == ZERO) || (K == 0)) && (Beta == ONE))) { + return; + } + + //* And when alpha.eq.zero. + if (Alpha == ZERO) { + if (UPPER) { + if (Beta == ZERO) { + for(j = 0 ; j < N; j++) { + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else { + for(j = 0 ; j < N ; j++) { + for(i = 0; i <= j ; i++) { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + else { + if (Beta == ZERO) { + for(j = 0 ; j < N ; j++) { + for(i = j ; i < N ; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else { + for(j = 0 ; j < N ; j++) { + for(i = j ; i < N ; i++) { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + } + return; + } + + //* Start the operations. + if(NOTRANS) { + //* Form C := alpha*A*A**T + beta*C. + if (UPPER) { + if(Beta == ZERO) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else if(Beta != ONE) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + + for(j = 0 ; j < N ; j++) { + for(l = 0; l < K ; l++) { + if (A[j*rsa + l*csa] != ZERO) { + tmp = Alpha*A[j*rsa + l*csa]; + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp*A[i*rsa + l*csa]; + } + } + } + } + } + else { + if(Beta == ZERO) { + for(j = 0; j < N ; j++) { + for(i = j ; i < N; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else if (Beta != ONE) { + for(j = 0; j < N ; j++) { + for(i = j; i < N; i++) { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + + for(j = 0; j < N ; j++) { + for(l = 0; l < K ; l++) { + if (A[j*rsa + l*csa] != ZERO) { + tmp = Alpha*A[j*rsa + l*csa]; + for(i = j ; i < N; i++) { + C[i*rsc + j*csc] = C[i*rsc + j*csc] + tmp*A[i*rsa + l*csa]; + } + } + } + } + } + } + else { + //* Form C := alpha*A**T*A + beta*C. + if (UPPER) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i <= j ; i++) { + tmp = ZERO; + for(l = 0; l < K ; l++) { + tmp = tmp + A[l*rsa + i*csa]*A[l*rsa + j*csa]; + } + if (Beta == ZERO) { + C[i*rsc + j*csc] = Alpha*tmp; + } + else { + C[i*rsc + j*csc] = Alpha*tmp + Beta*C[i*rsc + j*csc]; + } + } + } + } + else { + for(j = 0 ; j < N ; j++) { + for(i = j ; i < N ; i++) { + tmp = ZERO; + for(l = 0 ; l < K ; l++) { + tmp = tmp + A[l*rsa + i*csa]*A[l*rsa + j*csa]; + } + if (Beta == ZERO) { + C[i*rsc + j*csc] = Alpha*tmp; + } + else { + C[i*rsc + j*csc] = Alpha*tmp + Beta*C[i*rsc + j*csc]; + } + } + } + } + } + return; +} + +template +void libblis_icsyrk_check(uplo_t uplo, trans_t trans, dim_t N, dim_t K, + T* alpha, T* A, dim_t rsa, dim_t csa, T* beta, T* C, dim_t rsc, dim_t csc) { + + //* .. Local Scalars .. + T tmp; + dim_t i, j, l; + bool UPPER, NOTRANS; + T Alpha = *alpha; + T Beta = *beta; + + //* .. Parameters .. + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + + UPPER = (uplo == BLIS_UPPER); + NOTRANS = (trans == BLIS_NO_TRANSPOSE) || (trans == BLIS_CONJ_NO_TRANSPOSE); + + //* Quick return if possible. + if((N == 0) || + (((Alpha.real == ZERO.real) || (K == 0)) && (Beta.real == ONE.real))) { + return; + } + + //* And when alpha.eq.zero. + if((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)){ + if (UPPER) { + if((Beta.real == ZERO.real)&&(Beta.imag == ZERO.imag)) { + for(j = 0 ; j < N; j++) { + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else { + for(j = 0 ; j < N ; j++) { + for(i = 0; i <= j ; i++) { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + } + else { + if((Beta.real == ZERO.real)&&(Beta.imag == ZERO.imag)) { + for(j = 0 ; j < N ; j++) { + for(i = j ; i < N ; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else { + for(j = 0 ; j < N ; j++) { + for(i = j ; i < N ; i++) { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + } + return; + } + + //* Start the operations. + if(NOTRANS) { + //* Form C := alpha*A*A**T + beta*C. + if (UPPER) { + if((Beta.real == ZERO.real)||(Beta.imag == ZERO.imag)) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else if((Beta.real != ONE.real)||(Beta.imag != ONE.imag)) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + + for(j = 0 ; j < N ; j++) { + for(l = 0; l < K ; l++) { + if((A[j*rsa + l*csa].real != ZERO.real) || (A[j*rsa + l*csa].imag != ZERO.imag)) { + tmp = mulc(Alpha , A[j*rsa + l*csa]); + for(i = 0 ; i <= j ; i++) { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp , A[i*rsa + l*csa])); + } + } + } + } + } + else { + if((Beta.real == ZERO.real)||(Beta.imag == ZERO.imag)) { + for(j = 0; j < N ; j++) { + for(i = j ; i < N; i++) { + C[i*rsc + j*csc] = ZERO; + } + } + } + else if((Beta.real != ONE.real) || (Beta.imag != ONE.imag)){ + for(j = 0; j < N ; j++) { + for(i = j; i < N; i++) { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + + for(j = 0; j < N ; j++) { + for(l = 0; l < K ; l++) { + if((A[j*rsa + l*csa].real != ZERO.real)||(A[j*rsa + l*csa].imag != ZERO.imag)) { + tmp = mulc(Alpha , A[j*rsa + l*csa]); + for(i = j ; i < N; i++) { + C[i*rsc + j*csc] = addc(C[i*rsc + j*csc] , mulc(tmp , A[i*rsa + l*csa])); + } + } + } + } + } + } + else { + //* Form C := alpha*A**T*A + beta*C. + if (UPPER) { + for(j = 0 ; j < N ; j++) { + for(i = 0 ; i <= j ; i++) { + tmp = ZERO; + for(l = 0; l < K ; l++) { + tmp = addc(tmp , mulc(A[l*rsa + i*csa] , A[l*rsa + j*csa])); + } + if((Beta.real == ZERO.real) ||(Beta.imag == ZERO.imag)){ + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp) , mulc(Beta , C[i*rsc + j*csc])); + } + } + } + } + else { + for(j = 0 ; j < N ; j++) { + for(i = j ; i < N ; i++) { + tmp = ZERO; + for(l = 0 ; l < K ; l++) { + tmp = addc(tmp , mulc(A[l*rsa + i*csa] , A[l*rsa + j*csa])); + } + if((Beta.real == ZERO.real) || (Beta.imag == ZERO.imag)) { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else { + C[i*rsc + j*csc] = addc(mulc(Alpha , tmp) , mulc(Beta , C[i*rsc + j*csc])); + } + } + } + } + } + return; +} + +double libblis_test_isyrk_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig +){ + num_t dt = bli_obj_dt( a ); + dim_t M = bli_obj_length( c ); + dim_t K = bli_obj_width_after_trans( a ); + uplo_t uploc = bli_obj_uplo( c ); + trans_t transa = bli_obj_onlytrans_status( a ); + double resid = 0.0; + dim_t rsa, csa; + dim_t rsc, csc; + + rsa = bli_obj_row_stride( a ) ; + csa = bli_obj_col_stride( a ) ; + rsc = bli_obj_row_stride( c ) ; + csc = bli_obj_col_stride( c ) ; + + f77_int lda; + if( bli_obj_is_col_stored( c ) ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + int nrowa; + if (transa == BLIS_NO_TRANSPOSE) { + nrowa = M; + } else { + nrowa = K; + } + + if( lda < max(1, nrowa) ) { + return resid; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_isyrk_check(uploc, transa, M, K, Alpha, + A, rsa, csa, Beta, C, rsc, csc); + resid = computediffrm(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_isyrk_check(uploc, transa, M, K, Alpha, + A, rsa, csa, Beta, C, rsc, csc); + resid = computediffrm(M, M, CC, C, rsc, csc); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + libblis_icsyrk_check(uploc, transa, M, K, Alpha, + A, rsa, csa, Beta, C, rsc, csc); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + libblis_icsyrk_check(uploc, transa, M, K, Alpha, + A, rsa, csa, Beta, C, rsc, csc); + resid = computediffim(M, M, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + +template +double libblis_check_nan_real( dim_t rsc, dim_t csc, obj_t* c ) { + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + double resid = 0.0; + T* C = (T*) bli_obj_buffer( c ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rsc, dim_t csc, obj_t* c ) { + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + dim_t i,j; + double resid = 0.0; + U* C = (U*) bli_obj_buffer( c ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = C[ i*rsc + j*csc ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_syrk(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_is_col_stored( c ) ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_trmm.cpp b/gtestsuite/src/ref_trmm.cpp new file mode 100644 index 000000000..485325037 --- /dev/null +++ b/gtestsuite/src/ref_trmm.cpp @@ -0,0 +1,651 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trmm.h" + +using namespace std; + +//* ========================================================================== +//*> TRMM performs one of the matrix-matrix operations +//*> B := alpha*op( A )*B, or B := alpha*B*op( A ) +//*> where alpha is a scalar, B is an m by n matrix, A is a unit, or +//*> non-unit, upper or lower triangular matrix and op( A ) is one of +//*> op( A ) = A or op( A ) = A**T or op( A ) = A**H. +//* ========================================================================== + +template +void libblis_itrmm_check(side_t side, uplo_t uplo, trans_t transa, + diag_t diag, dim_t M, dim_t N, T Alpha, T* A, dim_t rsa, dim_t csa, + T* B, dim_t rsb, dim_t csb) +{ + T tmp; + int i, j, k; + bool LSIDE, NOUNIT, UPPER, NOTRANSA; + + T ONE = 1.0; + T ZERO = 0.0; + + LSIDE = ( side == BLIS_LEFT ); + NOTRANSA = ( transa == BLIS_NO_TRANSPOSE ); + NOUNIT = ( diag == BLIS_NONUNIT_DIAG ); + UPPER = ( uplo == BLIS_UPPER ); + + if( M == 0 || N == 0 ) + return; + + if( Alpha == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = ZERO; + } + } + return; + } + + + if( LSIDE ) + { + if( NOTRANSA ) + { + //* Form B := alpha*A*B. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( k = 0 ; k < M ; k++ ) + { + if( B[k*rsb + j*csb] != ZERO ) + { + tmp = Alpha*B[k*rsb + j*csb]; + for( i = 0 ; i < k ; i++ ) + { + B[i*rsb + j*csb] = B[i*rsb + j*csb] + tmp*A[i*rsa + k*csa]; + } + if( NOUNIT ) + tmp = tmp*A[k*rsa + k*csa]; + B[k*rsb + j*csb] = tmp; + } + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + for( k = (M-1) ; k >= 0 ; k-- ) + { + if( B[k*rsb + j*csb] != ZERO ) + { + tmp = Alpha*B[k*rsb + j*csb]; + B[k*rsb + j*csb] = tmp; + if( NOUNIT ) + B[k*rsb + j*csb] = B[k*rsb + j*csb]*A[k*rsa + k*csa]; + for( i = (k+1) ; i < M ; i++ ) + { + B[i*rsb + j*csb] = B[i*rsb + j*csb] + (tmp * A[i*rsa + k*csa]); + } + } + } + } + } + } + else + { + //* Form B := alpha*A**T*B. + if( UPPER ) + { + for( j = 0; j < N ; j++ ) + { + for( i = (M-1) ; i >= 0 ; i-- ) + { + tmp = B[i*rsb + j*csb]; + if( NOUNIT ) + tmp = tmp*A[i*rsa + i*csa]; + for( k = 0 ; k < i ; k++ ) + { + tmp = tmp + A[k*rsa + i*csa]*B[k*rsb + j*csb]; + } + B[i*rsb + j*csb] = Alpha*tmp; + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp = B[i*rsb + j*csb]; + if( NOUNIT ) + tmp = tmp*A[i*rsa + i*csa]; + for( k =(i+1) ; k < M ; k++ ) + { + tmp = tmp + A[k*rsa + i*csa]*B[k*rsb + j*csb]; + } + B[i*rsb + j*csb] = Alpha*tmp; + } + } + } + } + } + else + { + if( NOTRANSA ) + { + //* Form B := alpha*B*A. + if( UPPER ) + { + for( j = (N-1) ; j >= 0 ; j-- ) + { + tmp = Alpha; + if( NOUNIT ) + tmp = tmp*A[j*rsa + j*csa]; + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = tmp*B[i*rsb + j*csb]; + } + for( k = 0 ; k < j ; k++ ) + { + if( A[k*rsa + j*csa] != ZERO ) + { + tmp = Alpha*A[k*rsa + j*csa]; + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = B[i*rsb + j*csb] + tmp*B[i*rsb + k*csb]; + } + } + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + tmp = Alpha; + if( NOUNIT ) + tmp = tmp*A[j*rsa + j*csa]; + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = tmp*B[i*rsb + j*csb]; + } + for( k =(j+1) ; k < N ; k++ ) + { + if( A[k*rsa + j*csa] != ZERO ) + { + tmp = Alpha*A[k*rsa + j*csa]; + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = B[i*rsb + j*csb] + tmp*B[i*rsb + k*csb]; + } + } + } + } + } + } + else + { + //* Form B := alpha*B*A**T. + if( UPPER ) + { + for( k = 0 ; k < N ; k++ ) + { + for( j = 0 ; j < k ; j++ ) + { + if( A[j*rsa + k*csa] != ZERO ) + { + tmp = Alpha*A[j*rsa + k*csa]; + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = B[i*rsb + j*csb] + tmp*B[i*rsb + k*csb]; + } + } + } + tmp = Alpha; + if( NOUNIT ) + tmp = tmp*A[k*rsa + k*csa]; + if( tmp != ONE ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + k*csb] = tmp*B[i*rsb + k*csb]; + } + } + } + } + else + { + for( k = (N-1) ; k >= 0 ; k-- ) + { + for( j = (k+1) ; j < N ; j++ ) + { + if( A[j*rsa + k*csa] != ZERO ) + { + tmp = Alpha*A[j*rsa + k*csa]; + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = B[i*rsb + j*csb] + tmp*B[i*rsb + k*csb]; + } + } + } + tmp = Alpha; + if( NOUNIT ) + tmp = tmp*A[k*rsa + k*csa]; + if( tmp != ONE ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + k*csb] = tmp*B[i*rsb + k*csb]; + } + } + } + } + } + } + return; +} + +template +void libblis_ictrmm_check(side_t side, uplo_t uplo, trans_t transa, + diag_t diag, dim_t M, dim_t N, T Alpha, T* A, dim_t rsa, dim_t csa, + bool conja, T* B, dim_t rsb, dim_t csb) +{ + T tmp; + int i, j, k; + bool LSIDE, NOTRANSA, NOUNIT, UPPER; + + T ONE = { 1.0 , 0.0 }; + T ZERO = { 0.0 , 0.0 }; + + //* Test the input parameters. + LSIDE = ( side == BLIS_LEFT ); + NOTRANSA = ( transa == BLIS_NO_TRANSPOSE ); + NOUNIT = ( diag == BLIS_NONUNIT_DIAG ); + UPPER = ( uplo == BLIS_UPPER ); + + if( M == 0 || N == 0 ) + return; + + if( (Alpha.real == ZERO.real) || (Alpha.imag == ZERO.imag) ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = ZERO; + } + } + return; + } + + if( conja ) + { + dim_t dim; + if (LSIDE) dim = M; + else dim = N; + for( i = 0 ; i < dim ; i++ ) + { + for( j = 0 ; j < dim ; j++ ) + { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + if( LSIDE ) + { + if( NOTRANSA ) + { + //* Form B := alpha*A*B. + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( k = 0 ; k < M ; k++ ) + { + if( (B[k*rsb + j*csb].real != ZERO.real) || (B[k*rsb + j*csb].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , B[k*rsb + j*csb]); + for( i = 0 ; i < k ; i++ ) + { + B[i*rsb + j*csb] = addc(B[i*rsb + j*csb] , mulc(tmp , A[i*rsa + k*csa])); + } + if( NOUNIT ) + tmp = mulc(tmp , A[k*rsa + k*csa]); + B[k*rsb + j*csb] = tmp; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( k = (M-1) ; k >= 0 ; k-- ) + { + if( (B[k*rsb + j*csb].real != ZERO.real) || (B[k*rsb + j*csb].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , B[k*rsb + j*csb]); + B[k*rsb + j*csb] = tmp; + if( NOUNIT ) + B[k*rsb + j*csb] = mulc(B[k*rsb + j*csb] , A[k*rsa + k*csa]); + for( i = (k+1) ; i < M ; i++ ) + { + B[i*rsb + j*csb] = addc(B[i*rsb + j*csb] , mulc(tmp , A[i*rsa + k*csa])); + } + } + } + } + } + } + else + { + //* Form B := alpha*A**T*B or B := alpha*A**H*B. + if( UPPER ) + { + for( j = 0; j < N ; j++ ) + { + for( i = (M-1) ; i >= 0 ; i-- ) + { + tmp = B[i*rsb + j*csb]; + if( NOUNIT ) + tmp = mulc(tmp , A[i*rsa + i*csa]); + for( k = 0 ; k < i ; k++ ) + { + tmp = addc(tmp , mulc(A[k*rsa + i*csa] , B[k*rsb + j*csb])); + } + B[i*rsb + j*csb] = mulc(Alpha , tmp); + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp = B[i*rsb + j*csb]; + if( NOUNIT ) + tmp = mulc(tmp , A[i*rsa + i*csa]); + for( k =(i+1) ; k < M ; k++ ) + { + tmp = addc(tmp , mulc(A[k*rsa + i*csa] , B[k*rsb + j*csb])); + } + B[i*rsb + j*csb] = mulc(Alpha , tmp); + } + } + } + } + } + else + { + if( NOTRANSA ) + { + //* Form B := alpha*B*A. + if( UPPER ) + { + for( j = (N-1) ; j >= 0 ; j-- ) + { + tmp = Alpha; + if( NOUNIT ) + tmp = mulc(tmp , A[j*rsa + j*csa]); + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = mulc(tmp , B[i*rsb + j*csb]); + } + for( k = 0 ; k < j ; k++ ) + { + if( (A[k*rsa + j*csa].real != ZERO.real)||(A[k*rsa + j*csa].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , A[k*rsa + j*csa]); + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = addc(B[i*rsb + j*csb] , mulc(tmp , B[i*rsb + k*csb])); + } + } + } + } + } + else + { + for( j = 0; j < N ; j++ ) + { + tmp = Alpha; + if( NOUNIT ) + tmp = mulc(tmp , A[j*rsa + j*csa]); + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = mulc(tmp , B[i*rsb + j*csb]); + } + for( k =(j+1) ; k < N ; k++ ) + { + if( (A[k*rsa + j*csa].real != ZERO.real)||(A[k*rsa + j*csa].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , A[k*rsa + j*csa]); + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = addc(B[i*rsb + j*csb] , mulc(tmp , B[i*rsb + k*csb])); + } + } + } + } + } + } + else + { + //* Form B := alpha*B*A**T or B := alpha*B*A**H. + if( UPPER ) + { + for( k = 0 ; k < N ; k++ ) + { + for( j = 0 ; j < k ; j++ ) + { + if( (A[j*rsa + k*csa].real != ZERO.real)||(A[j*rsa + k*csa].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , A[j*rsa + k*csa]); + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = addc(B[i*rsb + j*csb] , mulc(tmp , B[i*rsb + k*csb])); + } + } + } + tmp = Alpha; + if( NOUNIT ) + tmp = mulc(tmp , A[k*rsa + k*csa]); + if( (tmp.real != ONE.real) || (tmp.imag != ONE.imag) ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + k*csb] = mulc(tmp , B[i*rsb + k*csb]); + } + } + } + } + else + { + for( k = (N-1) ; k >= 0 ; k-- ) + { + for( j = (k+1) ; j < N ; j++ ) + { + if( (A[j*rsa + k*csa].real != ZERO.real)||(A[j*rsa + k*csa].imag != ZERO.imag) ) + { + tmp = mulc(Alpha , A[j*rsa + k*csa]); + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + j*csb] = addc(B[i*rsb + j*csb] , mulc(tmp , B[i*rsb + k*csb])); + } + } + } + tmp = Alpha; + if( NOUNIT ) + tmp = mulc(tmp , A[k*rsa + k*csa]); + if( (tmp.real != ONE.real) || (tmp.imag != ONE.imag) ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsb + k*csb] = mulc(tmp , B[i*rsb + k*csb]); + } + } + } + } + } + } + return; +} + +double libblis_test_itrmm_check( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + num_t dt +){ + dim_t M = bli_obj_length( b_orig ); + dim_t N = bli_obj_width( b_orig ); + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_onlytrans_status( a ); + bool conja = bli_obj_has_conj( a ); + diag_t diaga = bli_obj_diag( a ); + dim_t rsa, csa; + dim_t rsb, csb; + double resid = 0.0; + + rsa = bli_obj_row_stride( a ) ; + csa = bli_obj_col_stride( a ) ; + rsb = bli_obj_row_stride( b ) ; + csb = bli_obj_col_stride( b ) ; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b_orig ); + float* BB = (float*) bli_obj_buffer( b ); + libblis_itrmm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, B, rsb, csb ); + resid = computediffrm(M, N, BB, B, rsb, csb); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b_orig ); + double* BB = (double*) bli_obj_buffer( b ); + libblis_itrmm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, B, rsb, csb ); + resid = computediffrm(M, N, BB, B, rsb, csb); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b_orig ); + scomplex* BB = (scomplex*) bli_obj_buffer( b ); + libblis_ictrmm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, conja, B, rsb, csb ); + resid = computediffim(M, N, BB, B, rsb, csb); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b_orig ); + dcomplex* BB = (dcomplex*) bli_obj_buffer( b ); + libblis_ictrmm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, conja, B, rsb, csb ); + resid = computediffim(M, N, BB, B, rsb, csb); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_trmm(obj_t* b, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( b ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( b ); + } else { + rsc = bli_obj_row_stride( b ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, b ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, b ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, b ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, b ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_trmm3.cpp b/gtestsuite/src/ref_trmm3.cpp new file mode 100644 index 000000000..42e04c479 --- /dev/null +++ b/gtestsuite/src/ref_trmm3.cpp @@ -0,0 +1,551 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trmm3.h" + +using namespace std; + +//* ========================================================================== +//*> TRMM performs one of the matrix-matrix operations +//*> C := beta * C_orig + alpha * transa(A) * transb(B) +//*> or +//*> C := beta * C_orig + alpha * transb(B) * transa(A) +//*> where alpha and beta are scalars, A is an triangular matrix and B and +//*> C are m by n matrices. +//* ========================================================================== + +template +void libblis_itrmm3_check(side_t side, uplo_t uplo, diag_t diaga, + dim_t M, dim_t N, T Alpha, T* A, dim_t rsa, dim_t csa, + T* B, dim_t rsb, dim_t csb, T Beta, T* C, dim_t rsc, dim_t csc ) +{ + T ONE = 1.0; + T ZERO = 0.0; + T tmp; + bool LSIDE, UPPER, UNITDA; + dim_t i, j, k; + + //* Test the input parameters. + LSIDE = ( side == BLIS_LEFT ); + UPPER = ( uplo == BLIS_UPPER ); + UNITDA = ( diaga == BLIS_UNIT_DIAG ); + + if( (M == 0 || N == 0) || ( Alpha == ZERO && Beta == ONE ) ) + return; + + //* And when Alpha.eq.zero. + if( Alpha == ZERO ) + { + if( Beta == ZERO ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc]; + } + } + } + return; + } + + if( UNITDA ) + { + dim_t dim; + if( LSIDE ) dim = M; + else dim = N; + for( i = 0 ; i < dim ; i++ ) + { + for( j = 0 ; j < dim ; j++ ) + { + if( i==j ) + A[i*rsa + j*csa] = ONE ; + } + } + } + + //* Start the operations. + if( LSIDE ) + { + //* Form C := beta * C_orig + alpha * transa(A) * transb(B) + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp = ZERO; + for( k = i ; k < M ; k++ ) + { + tmp += A[i*rsa + k*csa] * B[k*rsb + j*csb]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp; + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp = ZERO; + for( k = 0 ; k <= i ; k++ ) + { + tmp += A[i*rsa + k*csa] * B[k*rsb + j*csb]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp; + } + } + } + } + } + else + { + //* C := beta * C_orig + alpha * transb(B) * transa(A) + if( UPPER ) + { + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + tmp = ZERO ; + for( k = 0 ; k <= j ; k++ ) + { + tmp += B[i*rsb + k*csb]* A[k*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp; + } + } + } + } + else + { + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + tmp = ZERO ; + for( k = j ; k < N ; k++ ) + { + tmp += B[i*rsb + k*csb]* A[k*rsa + j*csa]; + } + if( Beta == ZERO ) + { + C[i*rsc + j*csc] = Alpha*tmp; + } + else + { + C[i*rsc + j*csc] = Beta*C[i*rsc + j*csc] + Alpha*tmp; + } + } + } + } + } + return; +} + +template +void libblis_ictrmm3_check(side_t side, uplo_t uplo, diag_t diaga, dim_t M, + dim_t N, T Alpha, T* A, dim_t rsa, dim_t csa, bool conja, T* B, dim_t rsb, + dim_t csb, bool conjb, T Beta, T* C, dim_t rsc, dim_t csc ) +{ + T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T tmp; + bool LSIDE, UPPER, UNITDA; + dim_t i, j, k; + + //* Test the input parameters. + LSIDE = ( side == BLIS_LEFT ); + UPPER = ( uplo == BLIS_UPPER ); + UNITDA = ( diaga == BLIS_UNIT_DIAG ); + + if( (M == 0 || N == 0) || ( Alpha.real == ZERO.real && Beta.real == ONE.real ) ) + return; + + if( conja ) + { + dim_t dim; + if( LSIDE ) dim = M; + else dim = N; + for( i = 0 ; i < dim ; i++ ) + { + for( j = 0 ; j < dim ; j++ ) + { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + if( conjb ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + B[i*rsc + j*csc] = conjugate(B[i*rsc + j*csc]); + } + } + } + + //* And when Alpha.eq.zero. + if( Alpha.real == ZERO.real ) + { + if( Beta.real == ZERO.real ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = ZERO; + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + C[i*rsc + j*csc] = mulc(Beta , C[i*rsc + j*csc]); + } + } + } + return; + } + + if( UNITDA ) + { + dim_t dim; + if( LSIDE ) dim = M; + else dim = N; + for( i = 0 ; i < dim ; i++ ) + { + for( j = 0 ; j < dim ; j++ ) + { + if( i==j ) + A[i*rsa + j*csa] = ONE ; + } + } + } + + //* Start the operations. + if( LSIDE ) + { + //* Form C := beta * C_orig + alpha * transa(A) * transb(B) + if( UPPER ) + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp = ZERO; + for( k = i ; k < M ; k++ ) + { + tmp = addc(tmp , mulc(A[i*rsa + k*csa] , B[k*rsb + j*csb])); + } + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else + { + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , mulc(Alpha , tmp)); + } + } + } + } + else + { + for( j = 0 ; j < N ; j++ ) + { + for( i = 0 ; i < M ; i++ ) + { + tmp = ZERO; + for( k = 0 ; k <= i ; k++ ) + { + tmp = addc(tmp , mulc(A[i*rsa + k*csa] , B[k*rsb + j*csb])); + } + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else + { + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , mulc(Alpha , tmp)); + } + } + } + } + } + else + { + //* C := beta * C_orig + alpha * transb(B) * transa(A) + if( UPPER ) + { + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + tmp = ZERO ; + for( k = 0 ; k <= j ; k++ ) + { + tmp = addc(tmp , mulc(B[i*rsb + k*csb] , A[k*rsa + j*csa])); + } + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else + { + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , mulc(Alpha , tmp)); + } + } + } + } + else + { + for( i = 0 ; i < M ; i++ ) + { + for( j = 0 ; j < N ; j++ ) + { + tmp = ZERO ; + for( k = j ; k < N ; k++ ) + { + tmp = addc(tmp , mulc(B[i*rsb + k*csb] , A[k*rsa + j*csa])); + } + if( (Beta.real == ZERO.real) || (Beta.imag == ZERO.imag) ) + { + C[i*rsc + j*csc] = mulc(Alpha , tmp); + } + else + { + C[i*rsc + j*csc] = addc(mulc(Beta , C[i*rsc + j*csc]) , mulc(Alpha , tmp)); + } + } + } + } + } + return; +} + +double libblis_test_itrmm3_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + diag_t diaga = bli_obj_diag( a ); + dim_t M = bli_obj_length( c ); + dim_t N = bli_obj_width( c ); + bool conja = bli_obj_has_conj( a ); + bool conjb = bli_obj_has_conj( b ); + trans_t transa = bli_obj_onlytrans_status( a ); + trans_t transb = bli_obj_onlytrans_status( b ); + dim_t rsa, csa; + dim_t rsb, csb; + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) + { + rsa = transa ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = transa ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + rsb = transb ? bli_obj_col_stride( b ) : bli_obj_row_stride( b ) ; + csb = transb ? bli_obj_row_stride( b ) : bli_obj_col_stride( b ) ; + rsc = 1; + csc = bli_obj_col_stride( c_orig ); + } + else + { + rsa = transa ? bli_obj_col_stride( a ) : bli_obj_row_stride( a ) ; + csa = transa ? bli_obj_row_stride( a ) : bli_obj_col_stride( a ) ; + rsb = transb ? bli_obj_col_stride( b ) : bli_obj_row_stride( b ) ; + csb = transb ? bli_obj_row_stride( b ) : bli_obj_col_stride( b ) ; + rsc = bli_obj_row_stride( c_orig ); + csc = 1 ; + } + + if( transa ) { + if( bli_obj_is_upper_or_lower( a ) ) { + bli_obj_toggle_uplo( a ); + } + uploa = bli_obj_uplo( a ); + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* C = (float*) bli_obj_buffer( c_orig ); + float* CC = (float*) bli_obj_buffer( c ); + libblis_itrmm3_check(side, uploa, diaga, M, N, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* C = (double*) bli_obj_buffer( c_orig ); + double* CC = (double*) bli_obj_buffer( c ); + libblis_itrmm3_check(side, uploa, diaga, M, N, *Alpha, + A, rsa, csa, B, rsb, csb, *Beta, C, rsc, csc ); + resid = computediffrm(M, N, CC, C, rsc, csc); + } + break; + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* C = (scomplex*) bli_obj_buffer( c_orig ); + scomplex* CC = (scomplex*) bli_obj_buffer( c ); + libblis_ictrmm3_check(side, uploa, diaga, M, N, + *Alpha, A, rsa, csa, conja, B, rsb, csb, conjb, *Beta, C, rsc, csc ); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* C = (dcomplex*) bli_obj_buffer( c_orig ); + dcomplex* CC = (dcomplex*) bli_obj_buffer( c ); + libblis_ictrmm3_check(side, uploa, diaga, M, N, + *Alpha, A, rsa, csa, conja, B, rsb, csb, conjb, *Beta, C, rsc, csc ); + resid = computediffim(M, N, CC, C, rsc, csc); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_trmm3(obj_t* c, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( c ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( c ); + } else { + rsc = bli_obj_row_stride( c ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, c ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, c ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/ref_trmv.cpp b/gtestsuite/src/ref_trmv.cpp new file mode 100644 index 000000000..33a89b135 --- /dev/null +++ b/gtestsuite/src/ref_trmv.cpp @@ -0,0 +1,278 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trmv.h" + +using namespace std; + +//* ========================================================================== +//*> TRMV performs one of the matrix-vector operations +//*> x := alpha * transa(A) * x +//*> where x is an n element vector and A is an n by n unit, or non-unit, +//*> upper or lower triangular matrix. +//* ========================================================================== + +template +void libblis_itrmv_check(uplo_t uploa, trans_t transa, diag_t diaga, + T* alpha, dim_t N, T* A, dim_t rsa, dim_t csa, T* X, dim_t incx){ + + T Alpha = *alpha; + T tmp; + int i, ix, j, jx, kx; + bool NOTRANS, NOUNIT; + + if (N == 0) + return; + + NOTRANS = (transa == BLIS_NO_TRANSPOSE); + NOUNIT = (diaga == BLIS_NONUNIT_DIAG); + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if(NOTRANS) { + //* Form x := A*x. + if(uploa == BLIS_UPPER){ + jx = kx; + for(j = 0 ; j < N ; j++){ + tmp = Alpha*X[jx]; + ix = kx; + for(i = 0 ; i < j ; i++) { + X[ix] = X[ix] + tmp*A[i*rsa + j*csa]; + ix = ix + incx; + } + if (NOUNIT) + tmp = tmp*A[j*rsa + j*csa]; + + X[jx] = tmp; + jx = jx + incx; + } + } + else{ + kx = kx + (N - 1)*incx; + jx = kx; + for(j = (N-1) ; j >= 0 ; j--){ + tmp = Alpha*X[jx]; + ix = kx; + for(i = (N-1) ; i > j ; i--){ + X[ix] = X[ix] + tmp*A[i*rsa + j*csa]; + ix = ix - incx; + } + if(NOUNIT) + tmp = tmp*A[j*rsa + j*csa]; + + X[jx] = tmp; + jx = jx - incx; + } + } + } + else { + //* Form x := A**T*x. + if(uploa == BLIS_UPPER){ + jx = kx + (N - 1)*incx; + for(j = (N-1) ; j >= 0 ; j--){ + tmp = X[jx]; + ix = jx; + if(NOUNIT) + tmp = tmp*A[j*rsa + j*csa]; + for(i = (j-1) ; i >= 0 ; i--) { + ix = ix - incx; + tmp = tmp + A[i*rsa + j*csa]*X[ix]; + } + X[jx] = Alpha*tmp; + jx = jx - incx; + } + } + else{ + jx = kx; + for(j = 0 ; j < N ; j++){ + tmp = X[jx]; + ix = jx; + if (NOUNIT) + tmp = tmp*A[j*rsa + j*csa]; + for(i = (j+1) ; i < N ; i++){ + ix = ix + incx; + tmp = tmp + X[ix]*A[i*rsa + j*csa]; + } + X[jx] = Alpha*tmp; + jx = jx + incx; + } + } + } + return; +} + +template +void libblis_ictrmv_check(uplo_t uploa, trans_t transa, diag_t diaga, +T* alpha, dim_t N, T* A, dim_t rsa, dim_t csa, bool conja, T* X, dim_t incx){ + + T Alpha = *alpha; + T tmp; + int i, ix, j, jx, kx; + bool NOTRANS, NOUNIT; + + if (N == 0) + return; + + NOTRANS = (transa == BLIS_NO_TRANSPOSE); + NOUNIT = (diaga == BLIS_NONUNIT_DIAG); + + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + if(conja) { + for(i = 0 ; i < N ; i++) { + for(j = 0 ; j < N ; j++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + if(NOTRANS){ + //* Form x := A*x. + if(uploa == BLIS_UPPER){ + jx = kx; + for(j = 0 ; j < N ; j++){ + tmp = mulc(Alpha , X[jx]); + ix = kx; + for(i = 0 ; i < j ; i++) { + X[ix] = addc(X[ix] , mulc(tmp , A[i*rsa + j*csa])); + ix = ix + incx; + } + if (NOUNIT) + tmp = mulc(tmp , A[j*rsa + j*csa]); + + X[jx] = tmp; + jx = jx + incx; + } + } + else{ + kx = kx + (N - 1)*incx; + jx = kx; + for(j = (N-1) ; j >= 0 ; j--){ + tmp = mulc(Alpha , X[jx]); + ix = kx; + for(i = (N-1) ; i > j ; i--){ + X[ix] = addc(X[ix] , mulc(tmp , A[i*rsa + j*csa])); + ix = ix - incx; + } + if(NOUNIT) + tmp = mulc(tmp , A[j*rsa + j*csa]); + + X[jx] = tmp; + jx = jx - incx; + } + } + } + else { + //* Form x := A**T*x. + if(uploa == BLIS_UPPER){ + jx = kx + (N - 1)*incx; + for(j = (N-1) ; j >= 0 ; j--){ + tmp = X[jx]; + ix = jx; + if(NOUNIT) + tmp = mulc(tmp , A[j*rsa + j*csa]); + for(i = (j-1) ; i >= 0 ; i--) { + ix = ix - incx; + tmp = addc(tmp , mulc(A[i*rsa + j*csa] , X[ix])); + } + X[jx] = mulc(Alpha , tmp); + jx = jx - incx; + } + } + else{ + jx = kx; + for(j = 0 ; j < N ; j++){ + tmp = X[jx]; + ix = jx; + if (NOUNIT) + tmp = mulc(tmp , A[j*rsa + j*csa]); + for(i = (j+1) ; i < N ; i++){ + ix = ix + incx; + tmp = addc(tmp , mulc(X[ix] , A[i*rsa + j*csa])); + } + X[jx] = mulc(Alpha , tmp); + jx = jx + incx; + } + } + } + return; +} + +double libblis_test_itrmv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +){ + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_length( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t rsa = bli_obj_row_stride( a ); + dim_t csa = bli_obj_col_stride( a ); + uplo_t uploa = bli_obj_uplo( a ); + bool conja = bli_obj_has_conj( a ); + trans_t transa = bli_obj_onlytrans_status( a ); + diag_t diaga = bli_obj_diag( a ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x_orig ); + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* XX = (float*) bli_obj_buffer( x ); + libblis_itrmv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, X, incx); + resid = computediffrv(M, incx, XX, X); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x_orig ); + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* XX = (double*) bli_obj_buffer( x ); + libblis_itrmv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, X, incx); + resid = computediffrv(M, incx, XX, X); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x_orig ); + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* XX = (scomplex*) bli_obj_buffer( x ); + libblis_ictrmv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, conja, X, incx); + resid = computediffiv(M, incx, XX, X); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x_orig ); + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* XX = (dcomplex*) bli_obj_buffer( x ); + libblis_ictrmv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, conja, X, incx); + resid = computediffiv(M, incx, XX, X); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + diff --git a/gtestsuite/src/ref_trsm.cpp b/gtestsuite/src/ref_trsm.cpp new file mode 100644 index 000000000..81077932c --- /dev/null +++ b/gtestsuite/src/ref_trsm.cpp @@ -0,0 +1,534 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trsm.h" + +using namespace std; + +//* ========================================================================== +//*> TRSM solves one of the matrix equations +//*> op( A )*X = alpha*B, or X*op( A ) = alpha*B, +//*> where alpha is a scalar, X and B are m by n matrices, A is a unit, or +//*> non-unit, upper or lower triangular matrix and op( A ) is one of +//*> op( A ) = A or op( A ) = A**T. +//*> The matrix X is overwritten on B. +//* ========================================================================== + +template +void libblis_ictrsm_check(side_t side, uplo_t uplo, trans_t transa, + diag_t diaga, dim_t M, dim_t N, T Alpha, T* A, dim_t rsa, dim_t csa, + bool conja, T* B, dim_t rsb, dim_t csb){ + + T tmp; + dim_t i, j, k; + bool LSIDE, NOUNIT, UPPER, NOTRANS; + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + + //* Test the input parameters. + LSIDE = (side == BLIS_LEFT); + NOTRANS = (transa == BLIS_NO_TRANSPOSE) || (transa == BLIS_CONJ_NO_TRANSPOSE); + NOUNIT = (diaga == BLIS_NONUNIT_DIAG); + UPPER = (uplo == BLIS_UPPER); + + //* Quick return if possible. + if ((M == 0) || (N == 0) ) + return; + + //* And when alpha.eq.zero. + if ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) { + for(i = 0; i < M ; i++) { + for(j = 0; j < N ; j++) { + B[i*rsb+ j*csb] = ZERO; + } + } + return; + } + + if(conja) { + dim_t dim; + if (LSIDE) dim = M; + else dim = N; + for( i = 0 ; i < dim ; i++ ) { + for( j = 0 ; j < dim ; j++ ) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + if((Alpha.real != ONE.real)&&(Alpha.imag != ONE.imag)){ + for(i = 0; i < M; i++) { + for(j = 0 ; j < N ; j++) { + B[i*rsb + j*csb] = mulc(Alpha , B[i*rsb + j*csb]); + } + } + } + + //* Start the operations. + if (LSIDE) { + if (NOTRANS) { + //* Form B := alpha*inv( A )*B. + if (UPPER) { /* AuXB : LUN */ + for(j = 0 ; j < N ; j++) { + for(k = M; k-- ; ) { + if((B[k*rsb + j*csb].real != ZERO.real) || (B[k*rsb + j*csb].imag != ZERO.imag)) { + if (NOUNIT) B[k*rsb + j*csb] = divc(B[k*rsb + j*csb] , A[k*rsa + k*csa]); + for(i = 0 ; i < k ; i++) { + B[i*rsb + j*csb] = subc(B[i*rsb + j*csb] , mulc(B[k*rsb + j*csb] , A[i*rsa + k*csa])); + } + } + } + } + } + else { + for(j = 0 ; j < N ; j++) { /* AlXB : LLN */ + for(k = 0 ; k < M ; k++) { + if ((B[k*rsb + j*csb].real != ZERO.real) || (B[k*rsb + j*csb].imag != ZERO.imag)) { + if (NOUNIT) B[k*rsb + j*csb] = divc(B[k*rsb + j*csb] , A[k*rsa + k*csa]); + for(i=(k+1) ; i < M ; i++) { + B[i*rsb + j*csb] = subc(B[i*rsb + j*csb] , mulc(B[k*rsb + j*csb] , A[i*rsa + k*csa])); + } + } + } + } + } + } + else { + //* Form B := alpha*inv( A**T )*B. + if (UPPER) { + for(j = 0 ; j < N ; j++) { /* AutXB : LUT */ + for(i = 0 ; i < M ; i++) { + tmp = B[i*rsb + j*csb]; + for(k = 0 ; k < i ; k++) { + tmp = subc(tmp , mulc(A[k*rsa + i*csa] , B[k*rsb + j*csb])); + } + if (NOUNIT) tmp = divc(tmp , A[i*rsa + i*csa]); + B[i*rsb + j*csb] = tmp; + } + } + } + else { + for(j = 0 ; j < N ; j++) { /* AltXB : LLT */ + for(i = M ; i-- ;) { + tmp = B[i*rsb + j*csb]; + for(k = (i+1) ; k < M ; k++) { + tmp = subc(tmp , mulc(A[k*rsa + i*csa] , B[k*rsb + j*csb])); + } + if (NOUNIT) tmp = divc(tmp , A[i*rsa + i*csa]); + B[i*rsb + j*csb] = tmp; + } + } + } + } + } + else { + if(NOTRANS) { + //* Form B := alpha*B*inv( A ). + if (UPPER) { + for(j = 0 ; j < N ; j++) { /* XAuB : RUN */ + for(k = 0 ; k < j ; k++) { + if ((A[k*rsa + j*csa].real != ZERO.real)||(A[k*rsa + j*csa].imag != ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = subc(B[i*rsb + j*csb] , mulc(A[k*rsa + j*csa] , B[i*rsb + k*csb])); + } + } + } + if (NOUNIT) { + tmp = divc(ONE , A[j*rsa + j*csa]); + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = mulc(tmp , B[i*rsb + j*csb]); + } + } + } + } + else { + for(j = N; j-- ; ) { /* XAlB : RLN */ + for(k = (j+1) ; k < N ; k++) { + if((A[k*rsa + j*csa].real != ZERO.real)||(A[k*rsa + j*csa].imag != ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = subc(B[i*rsb + j*csb] , mulc(A[k*rsa + j*csa] , B[i*rsb + k*csb])); + } + } + } + if (NOUNIT) { + tmp = divc(ONE , A[j*rsa + j*csa]); + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = mulc(tmp , B[i*rsb + j*csb]); + } + } + } + } + } + else { + //* Form B := alpha*B*inv( A**T ). + if (UPPER) { /* XAutB : RUT */ + for(k = N ; k-- ; ) { + if (NOUNIT) { + tmp = divc(ONE , A[k*rsa + k*csa]); + for(i = 0 ; i < M ; i++) { + B[i*rsb + k*csb] = mulc(tmp , B[i*rsb + k*csb]); + } + } + for(j = 0 ; j < k; j++) { + if((A[j*rsa + k*csa].real != ZERO.real)||(A[j*rsa + k*csa].imag != ZERO.imag)) { + tmp = A[j*rsa + k*csa]; + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = subc(B[i*rsb + j*csb] , mulc(tmp , B[i*rsb + k*csb])); + } + } + } + } + } + else { /* XAltB : RLT */ + for(k = 0 ; k < N; k++) { + if (NOUNIT) { + tmp = divc(ONE , A[k*rsa + k*csa]); + for(i = 0 ; i < M ; i++) { + B[i*rsb + k*csb] = mulc(tmp , B[i*rsb + k*csb]); + } + } + for(j = (k+1) ; j < N ; j++) { + if((A[j*rsa + k*csa].real != ZERO.real)||(A[j*rsa + k*csa].imag != ZERO.imag)) { + tmp = A[j*rsa + k*csa]; + for(i = 0 ; i < M; i++) { + B[i*rsb + j*csb] = subc(B[i*rsb + j*csb] , mulc(tmp , B[i*rsb + k*csb])); + } + } + } + } + } + } + } + return; +} + +template +void libblis_itrsm_check(side_t side, uplo_t uploa, trans_t transa, + diag_t diaga, dim_t M, dim_t N, T Alpha, T* A, dim_t rsa, dim_t csa, + T* B, dim_t rsb, dim_t csb) { + + T tmp; + dim_t i, j, k; + bool LSIDE, UPPER; + bool NOTRANS, NOUNIT; + T ONE = 1.0; + T ZERO = 0.0; + + LSIDE = (side == BLIS_LEFT); + NOTRANS = (transa == BLIS_NO_TRANSPOSE) || (transa == BLIS_CONJ_NO_TRANSPOSE); + NOUNIT = (diaga == BLIS_NONUNIT_DIAG); + UPPER = (uploa == BLIS_UPPER); + + if((M == 0) || (N == 0)) + return; + + if (Alpha == ZERO) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N; j++) { + B[i*rsb + j*csb] = ZERO; + } + } + return; + } + + if (Alpha != ONE) { + for(i = 0; i < M; i++) { + for(j = 0 ; j < N ; j++) { + B[i*rsb + j*csb] = Alpha*B[i*rsb + j*csb]; + } + } + } + + //* Start the operations. + if (LSIDE) { + if (NOTRANS) { + //* Form B := alpha*inv( A )*B. + if (UPPER) { /* AuXB : LUN */ + for(j = 0 ; j < N ; j++) { + for(k = M; k-- ; ) { + if (B[k*rsb + j*csb] != ZERO) { + if (NOUNIT) B[k*rsb + j*csb] = B[k*rsb + j*csb]/A[k*rsa + k*csa]; + for(i = 0 ; i < k ; i++) { + B[i*rsb + j*csb] = B[i*rsb + j*csb] - B[k*rsb + j*csb]*A[i*rsa + k*csa]; + } + } + } + } + } + else { + for(j = 0 ; j < N ; j++) { /* AlXB : LLN */ + for(k = 0 ; k < M ; k++) { + if (B[k*rsb + j*csb] != ZERO) { + if (NOUNIT) B[k*rsb + j*csb] = B[k*rsb + j*csb]/A[k*rsa + k*csa]; + for(i=(k+1) ; i < M ; i++) { + B[i*rsb + j*csb] = B[i*rsb + j*csb] - (B[k*rsb + j*csb]*A[i*rsa + k*csa]); + } + } + } + } + } + } + else { + //* Form B := alpha*inv( A**T )*B. + if (UPPER) { + for(j = 0 ; j < N ; j++) { /* AutXB : LUT */ + for(i = 0 ; i < M ; i++) { + tmp = B[i*rsb + j*csb]; + for(k = 0 ; k < i ; k++) { + tmp = tmp - A[k*rsa + i*csa]*B[k*rsb + j*csb]; + } + if (NOUNIT) tmp = tmp/A[i*rsa + i*csa]; + B[i*rsb + j*csb] = tmp; + } + } + } + else { + for(j = 0 ; j < N ; j++) { /* AltXB : LLT */ + for(i = M ; i-- ;) { + tmp = B[i*rsb + j*csb]; + for(k = (i+1) ; k < M ; k++) { + tmp = tmp - A[k*rsa + i*csa]*B[k*rsb + j*csb]; + } + if (NOUNIT) tmp = tmp/A[i*rsa + i*csa]; + B[i*rsb + j*csb] = tmp; + } + } + } + } + } + else { + if(NOTRANS) { + //* Form B := alpha*B*inv( A ). + if (UPPER) { + for(j = 0 ; j < N ; j++) { /* XAuB : RUN */ + for(k = 0 ; k < j ; k++) { + if (A[k*rsa + j*csa] != ZERO) { + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = B[i*rsb + j*csb] - A[k*rsa + j*csa]*B[i*rsb + k*csb]; + } + } + } + if (NOUNIT) { + tmp = ONE/A[j*rsa + j*csa]; + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = tmp*B[i*rsb + j*csb]; + } + } + } + } + else { + for(j = N; j-- ; ) { /* XAlB : RLN */ + for(k = (j+1) ; k < N ; k++) { + if (A[k*rsa + j*csa] != ZERO) { + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = B[i*rsb + j*csb] - A[k*rsa + j*csa]*B[i*rsb + k*csb]; + } + } + } + if (NOUNIT) { + tmp = ONE/A[j*rsa + j*csa]; + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = tmp*B[i*rsb + j*csb]; + } + } + } + } + } + else { + //* Form B := alpha*B*inv( A**T ). + if (UPPER) { /* XAutB : RUT */ + for(k = N ; k-- ; ) { + if (NOUNIT) { + tmp = ONE/A[k*rsa + k*csa]; + for(i = 0 ; i < M ; i++) { + B[i*rsb + k*csb] = tmp*B[i*rsb + k*csb]; + } + } + for(j = 0 ; j < k; j++) { + if (A[j*rsa + k*csa] != ZERO) { + tmp = A[j*rsa + k*csa]; + for(i = 0 ; i < M ; i++) { + B[i*rsb + j*csb] = B[i*rsb + j*csb] - tmp*B[i*rsb + k*csb]; + } + } + } + } + } + else { /* XAltB : RLT */ + for(k = 0 ; k < N; k++) { + if (NOUNIT) { + tmp = ONE/A[k*rsa + k*csa]; + for(i = 0 ; i < M ; i++) { + B[i*rsb + k*csb] = tmp*B[i*rsb + k*csb]; + } + } + for(j = (k+1) ; j < N ; j++) { + if (A[j*rsa + k*csa] != ZERO) { + tmp = A[j*rsa + k*csa]; + for(i = 0 ; i < M; i++) { + B[i*rsb + j*csb] = B[i*rsb + j*csb] - tmp*B[i*rsb + k*csb]; + } + } + } + } + } + } + } + return; +} + +double libblis_test_itrsm_check( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + num_t dt +){ + dim_t M = bli_obj_length( b_orig ); + dim_t N = bli_obj_width( b_orig ); + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_onlytrans_status( a ); + bool conja = bli_obj_has_conj( a ); + diag_t diaga = bli_obj_diag( a ); + dim_t rsa, csa; + dim_t rsb, csb; + double resid = 0.0; + + rsa = bli_obj_row_stride( a ) ; + csa = bli_obj_col_stride( a ) ; + rsb = bli_obj_row_stride( b ) ; + csb = bli_obj_col_stride( b ) ; + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* A = (float*) bli_obj_buffer( a ); + float* B = (float*) bli_obj_buffer( b_orig ); + float* BB = (float*) bli_obj_buffer( b ); + libblis_itrsm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, B, rsb, csb ); + resid = computediffrm(M, N, BB, B, rsb, csb); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* A = (double*) bli_obj_buffer( a ); + double* B = (double*) bli_obj_buffer( b_orig ); + double* BB = (double*) bli_obj_buffer( b ); + libblis_itrsm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, B, rsb, csb ); + resid = computediffrm(M, N, BB, B, rsb, csb); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* B = (scomplex*) bli_obj_buffer( b_orig ); + scomplex* BB = (scomplex*) bli_obj_buffer( b ); + libblis_ictrsm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, conja, B, rsb, csb ); + resid = computediffim(M, N, BB, B, rsb, csb); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* B = (dcomplex*) bli_obj_buffer( b_orig ); + dcomplex* BB = (dcomplex*) bli_obj_buffer( b ); + libblis_ictrsm_check(side, uploa, transa, + diaga, M, N, *Alpha, A, rsa, csa, conja, B, rsb, csb ); + resid = computediffim(M, N, BB, B, rsb, csb); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + +template +double libblis_check_nan_real( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv )) { + resid = tv ; + break; + } + } + } + return resid; +} + +template +double libblis_check_nan_complex( dim_t rs, dim_t cs, obj_t* b ) { + dim_t M = bli_obj_length( b ); + dim_t N = bli_obj_width( b ); + dim_t i,j; + double resid = 0.0; + T* B = (T*) bli_obj_buffer( b ); + + for( i = 0 ; i < M ; i++ ) { + for( j = 0 ; j < N ; j++ ) { + auto tv = B[ i*rs + j*cs ]; + if ( bli_isnan( tv.real ) || bli_isnan( tv.imag )) { + resid = bli_isnan( tv.real ) ? tv.real : tv.imag; + break; + } + } + } + return resid; +} + +double libblis_check_nan_trsm(obj_t* b, num_t dt ) { + dim_t rsc, csc; + double resid = 0.0; + + if( bli_obj_row_stride( b ) == 1 ) { + rsc = 1; + csc = bli_obj_col_stride( b ); + } else { + rsc = bli_obj_row_stride( b ); + csc = 1 ; + } + + switch( dt ) { + case BLIS_FLOAT: + { + resid = libblis_check_nan_real( rsc, csc, b ); + break; + } + case BLIS_DOUBLE: + { + resid = libblis_check_nan_real( rsc, csc, b ); + break; + } + case BLIS_SCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, b ); + break; + } + case BLIS_DCOMPLEX: + { + resid = libblis_check_nan_complex( rsc, csc, b ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_trsv.cpp b/gtestsuite/src/ref_trsv.cpp new file mode 100644 index 000000000..507f931a1 --- /dev/null +++ b/gtestsuite/src/ref_trsv.cpp @@ -0,0 +1,293 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trsv.h" + +using namespace std; + +//* ========================================================================== +//*> TRSV Solves a triangular system of equations with a single value for the +//*> right side +//*> x := alpha * inv(transa(A)) * x_orig +//*> where b and x are n element vectors and A is an n by n unit, or +//*> non-unit, upper or lower triangular matrix. +//* ========================================================================== + +template +void libblis_itrsv_check(uplo_t uploa, trans_t transa, diag_t diaga, + T* alpha, dim_t N, T* A, dim_t rsa, dim_t csa, T* X, dim_t incx){ + + T Alpha = alpha[0]; + T tmp; + int i, ix, j, jx, kx; + bool NOTRANS, NOUNIT; + + if(N == 0) + return; + + NOTRANS = (transa == BLIS_NO_TRANSPOSE); + NOUNIT = (diaga == BLIS_NONUNIT_DIAG); + + //* Set up the start point in X if the increment is not unity. This + //* will be ( N - 1 )*incx too small for descending loops. + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = (Alpha * X[ix]); + ix = ix + incx; + } + + //* Start the operations. In this version the elements of A are + //* accessed sequentially with one pass through A. + if(NOTRANS){ + //* Form x := inv( A )*x. + if(uploa == BLIS_UPPER){ + kx = kx + (N - 1)*incx; + ix = kx; + for(i = (N-1) ; i >= 0 ; i--){ + tmp = 0; + jx = (ix+1); + for(j = (i+1) ; j < N ; j++){ + tmp = tmp + X[jx]*A[i*rsa + j*csa]; + jx = jx + incx; + } + tmp = (X[ix] - tmp); + if(NOUNIT) + tmp = (tmp/A[i*rsa + i*csa]); + X[ix] = tmp; + ix = ix - incx; + } + } + else{ + ix = kx; + for(i = 0 ; i < N ; i++){ + tmp = 0; + jx = kx; + for(j = 0 ; j < i ; j++ ){ + tmp = tmp + (X[jx]*A[i*rsa + j*csa]); + jx = jx + incx; + } + tmp = (X[ix] - tmp); + if(NOUNIT) + tmp = (tmp/A[i*rsa + i*csa]); + X[ix] = tmp; + ix = ix + incx; + } + } + } + else{ + //* Form x := inv( A**T )*x. + if(uploa == BLIS_UPPER){ + ix = kx; + for(i = 0 ; i < N ; i++){ + if(NOUNIT) + X[ix] = (X[ix]/A[i*rsa + i*csa]); + tmp = X[ix]; + jx = ix; + for(j = (i+1) ; j < N ; j++){ + jx = jx + incx; + X[jx] = X[jx] - (tmp * A[i*rsa + j*csa]); + } + ix = ix + incx; + } + } + else{ + ix = kx + (N - 1)*incx; + for(i = (N-1) ; i >= 0 ; i--){ + if(NOUNIT) + X[ix] = (X[ix]/A[i*rsa + i*csa]); + tmp = X[ix]; + jx = ix; + for(j = (i-1) ; j >= 0 ; j--){ + jx = jx - incx; + X[jx] = X[jx] - (tmp * A[i*rsa + j*csa]); + } + ix = ix - incx; + } + } + } + return; +} + +template +void libblis_ictrsv_check(uplo_t uploa, trans_t transa, diag_t diaga, +T* alpha, dim_t N, T* A, dim_t rsa, dim_t csa, bool conja, T* X, dim_t incx){ + + T Alpha = *alpha; + T tmp; + int i, ix, j, jx, kx; + bool NOTRANS, NOUNIT; + + if (N == 0) + return; + + NOTRANS = (transa == BLIS_NO_TRANSPOSE); + NOUNIT = (diaga == BLIS_NONUNIT_DIAG); + + //* Set up the start point in X if the increment is not unity. This + //* will be ( N - 1 )*incx too small for descending loops. + if (incx > 0) { + kx = 0; + } + else { + kx = 1 - (N * incx); + } + + ix = 0; + for(i = 0 ; i < N ; i++) { + X[ix] = mulc(Alpha , X[ix]); + ix = ix + incx; + } + + if(conja) { + for(i = 0 ; i < N ; i++) { + for(j = 0 ; j < N ; j++) { + A[i*rsa + j*csa] = conjugate(A[i*rsa + j*csa]); + } + } + } + + if(NOTRANS){ + //* Form x := inv( A )*x. + if(uploa == BLIS_UPPER){ + kx = kx + (N - 1)*incx; + ix = kx; + for(i = (N-1) ; i >= 0 ; i--){ + tmp = {0.0,0.0}; + jx = (ix+1); + for(j = (i+1) ; j < N ; j++){ + tmp = addc(tmp , mulc(X[jx] , A[i*rsa + j*csa])); + jx = jx + incx; + } + tmp = subc(X[ix] , tmp); + if(NOUNIT) + tmp = divc(tmp , A[i*rsa + i*csa]); + X[ix] = tmp; + ix = ix - incx; + } + } + else{ + ix = kx; + for(i = 0 ; i < N ; i++){ + tmp = {0.0,0.0}; + jx = kx; + for(j = 0 ; j < i ; j++ ){ + tmp = addc(tmp , mulc(X[jx] , A[i*rsa + j*csa])); + jx = jx + incx; + } + tmp = subc(X[ix] , tmp); + if(NOUNIT) + tmp = divc(tmp , A[i*rsa + i*csa]); + X[ix] = tmp; + ix = ix + incx; + } + } + } + else{ + //* Form x := inv( A**T )*x. + if(uploa == BLIS_UPPER){ + ix = kx; + for(i = 0 ; i < N ; i++){ + if(NOUNIT) + X[ix] = divc(X[ix] , A[i*rsa + i*csa]); + tmp = X[ix]; + jx = ix; + for(j = (i+1) ; j < N ; j++){ + jx = jx + incx; + X[jx] = subc(X[jx] , mulc(tmp , A[i*rsa + j*csa])); + } + ix = ix + incx; + } + } + else{ + ix = kx + (N - 1)*incx; + for(i = (N-1) ; i >= 0 ; i--){ + if(NOUNIT) + X[ix] = divc(X[ix] , A[i*rsa + i*csa]); + tmp = X[ix]; + jx = ix; + for(j = (i-1) ; j >= 0 ; j--){ + jx = jx - incx; + X[jx] = subc(X[jx] , mulc(tmp , A[i*rsa + j*csa])); + } + ix = ix - incx; + } + } + } + return; +} + +double libblis_test_itrsv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +){ + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_length( a ); + dim_t incx = bli_obj_vector_inc( x ); + dim_t rsa = bli_obj_row_stride( a ) ; + dim_t csa = bli_obj_col_stride( a ) ; + uplo_t uploa = bli_obj_uplo( a ); + bool conja = bli_obj_has_conj( a ); + trans_t transa = bli_obj_onlytrans_status( a ); + diag_t diaga = bli_obj_diag( a ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x_orig ); + float* A = (float*) bli_obj_buffer( a ); + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* XX = (float*) bli_obj_buffer( x ); + libblis_itrsv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, X, incx); + resid = computediffrv(M, incx, XX, X); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x_orig ); + double* A = (double*) bli_obj_buffer( a ); + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* XX = (double*) bli_obj_buffer( x ); + libblis_itrsv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, X, incx); + resid = computediffrv(M, incx, XX, X); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x_orig ); + scomplex* A = (scomplex*) bli_obj_buffer( a ); + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* XX = (scomplex*) bli_obj_buffer( x ); + libblis_ictrsv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, conja, X, incx); + resid = computediffiv(M, incx, XX, X); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x_orig ); + dcomplex* A = (dcomplex*) bli_obj_buffer( a ); + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* XX = (dcomplex*) bli_obj_buffer( x ); + libblis_ictrsv_check(uploa, transa, diaga, Alpha, + M, A, rsa, csa, conja, X, incx); + resid = computediffiv(M, incx, XX, X); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return resid; +} + diff --git a/gtestsuite/src/ref_xpbym.cpp b/gtestsuite/src/ref_xpbym.cpp new file mode 100644 index 000000000..759025c7e --- /dev/null +++ b/gtestsuite/src/ref_xpbym.cpp @@ -0,0 +1,176 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_xpbym.h" + +using namespace std; + +//* ========================================================================== +//*> XPBYM performs vector operations +//*> B := B * alpha + conjx(A) +//*> where B is an m x n matrix. +//* ========================================================================== + +template +void libblis_ixpbym_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, T* Y, dim_t rsy, dim_t csy, T* YY) { + + dim_t i, j; + T ONE = 1.0 ; + T ZERO = 0.0 ; + T Alpha = alpha[0]; + + if ((M == 0) || (N == 0)) { + return; + } + + //* First form y := beta*y. + if (Alpha != ONE) { + if (Alpha == ZERO) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = ZERO; + } + } + } + else { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = (Alpha * Y[i*rsy + j*csy]); + } + } + } + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = Y[i*rsy + j*csy] + X[i*rsx + j*csx] ; + } + } + + return; +} + +template +void libblis_icxpbym_check(dim_t M, dim_t N, T* alpha, + T* X, dim_t rsx, dim_t csx, conj_t conjx, T* Y, dim_t rsy, dim_t csy) { + + dim_t i, j; + T ONE = {1.0 , 0.0}; + T ZERO = {0.0 , 0.0}; + T Alpha = *alpha; + + if ((M == 0) || (N == 0)) { + return; + } + + if(conjx) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + X[i*rsx + j*csx] = conjugate(X[i*rsx + j*csx]); + } + } + } + + if ((Alpha.real != ONE.real) && (Alpha.imag != ONE.imag)) { + if ((Alpha.real == ZERO.real) && (Alpha.imag == ZERO.imag)) { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = ZERO; + } + } + } + else { + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = mulc(Alpha , Y[i*rsy + j*csy]); + } + } + } + } + + for(i = 0 ; i < M ; i++) { + for(j = 0 ; j < N ; j++) { + Y[i*rsy + j*csy] = addc(Y[i*rsy + j*csy] , X[i*rsx + j*csx]); + } + } + + return; +} + +double libblis_test_ixpbym_check( + test_params_t* params, + obj_t* x, + obj_t* alpha, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + dim_t M = bli_obj_length( y ); + dim_t N = bli_obj_width( y ); + bool transx = bli_obj_has_trans( x ); + conj_t conjx = bli_obj_conj_status( x ); + dim_t rsy = bli_obj_row_stride( y ) ; + dim_t csy = bli_obj_col_stride( y ) ; + double resid = 0.0; + dim_t rsx, csx; + + if( bli_obj_is_col_stored( x ) ) { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } else { + rsx = transx ? bli_obj_col_stride( x ) : bli_obj_row_stride( x ) ; + csx = transx ? bli_obj_row_stride( x ) : bli_obj_col_stride( x ) ; + } + + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* X = (float*) bli_obj_buffer( x ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_ixpbym_check( M, N, Alpha, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* X = (double*) bli_obj_buffer( x ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_ixpbym_check( M, N, Alpha, X, rsx, csx, + Y, rsy, csy, YY ); + resid = computediffrm(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icxpbym_check( M, N, Alpha, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icxpbym_check( M, N, Alpha, X, rsx, csx, + conjx, Y, rsy, csy ); + resid = computediffim(M, N, YY, Y, rsy, csy); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/ref_xpbyv.cpp b/gtestsuite/src/ref_xpbyv.cpp new file mode 100644 index 000000000..71aea383c --- /dev/null +++ b/gtestsuite/src/ref_xpbyv.cpp @@ -0,0 +1,165 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_xpbyv.h" + +using namespace std; + +//* ========================================================================== +//*> XPBYV performs vector operations +//*> y := beta * y + conjx(x) +//*> where x and y are vectors of length n, and beta is scalar +//* ========================================================================== + +template +void libblis_ixpbyv_check(dim_t len, T* X, dim_t incx, + T* beta, T* Y, dim_t incy) { + + dim_t i, ix, iy; + T ONE, ZERO; + ONE = 1.0 ; + ZERO = 0.0 ; + T Beta = beta[0]; + if ((len == 0) || (Beta == ONE)){ + return; + } + + //* First form y := beta*y. + if (Beta != ONE) { + iy = 0; + if (Beta == ZERO) { + for(i = 0 ; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = Beta*Y[iy]; + iy = iy + incy; + } + } + } + + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = Y[iy] + X[ix]; + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +template +void libblis_icxpbyv_check(dim_t len, T* X, dim_t incx, + T* beta, T* Y, dim_t incy, bool cfx) { + dim_t i, ix, iy; + T ONE, ZERO; + ONE = {1.0 , 0.0}; + ZERO = {0.0 , 0.0}; + T Beta = *beta; + + if (len == 0) { + return; + } + + if(cfx) { + ix = 0; + for(i = 0 ; i < len ; i++) { + X[ix] = conjugate(X[ix]); + ix = ix + incx; + } + } + + /* First form y := beta*y. */ + iy = 0; + if ((Beta.real == ZERO.real) && (Beta.imag == ZERO.imag)) { + for(i = 0; i < len ; i++) { + Y[iy] = ZERO; + iy = iy + incy; + } + } + else { + for(i = 0 ; i < len ; i++) { + Y[iy] = mulc(Beta , Y[iy]); + iy = iy + incy; + } + } + + ix = 0; + iy = 0; + for(i = 0 ; i < len ; i++) { + Y[iy] = addc(Y[iy] , X[ix]); + ix = ix + incx; + iy = iy + incy; + } + + return; +} + +double libblis_test_ixpbyv_check( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( x ); + dim_t M = bli_obj_vector_dim( x ); + bool cfx = bli_obj_has_conj( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y_orig ); + double resid = 0.0; + + switch( dt ) { + case BLIS_FLOAT : + { + float* X = (float*) bli_obj_buffer( x ); + float* Beta = (float*) bli_obj_buffer( beta ); + float* Y = (float*) bli_obj_buffer( y_orig ); + float* YY = (float*) bli_obj_buffer( y ); + libblis_ixpbyv_check( M, X, incx, Beta, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_DOUBLE : + { + double* X = (double*) bli_obj_buffer( x ); + double* Beta = (double*) bli_obj_buffer( beta ); + double* Y = (double*) bli_obj_buffer( y_orig ); + double* YY = (double*) bli_obj_buffer( y ); + libblis_ixpbyv_check( M, X, incx, Beta, Y, incy ); + resid = computediffrv(M, incy, YY, Y); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* X = (scomplex*) bli_obj_buffer( x ); + scomplex* Beta = (scomplex*) bli_obj_buffer( beta ); + scomplex* Y = (scomplex*) bli_obj_buffer( y_orig ); + scomplex* YY = (scomplex*) bli_obj_buffer( y ); + libblis_icxpbyv_check( M, X, incx, Beta, + Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* X = (dcomplex*) bli_obj_buffer( x ); + dcomplex* Beta = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* Y = (dcomplex*) bli_obj_buffer( y_orig ); + dcomplex* YY = (dcomplex*) bli_obj_buffer( y ); + libblis_icxpbyv_check( M, X, incx, Beta, + Y, incy, cfx ); + resid = computediffiv(M, incy, YY, Y); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return resid; +} + diff --git a/gtestsuite/src/test_addm.cpp b/gtestsuite/src/test_addm.cpp new file mode 100644 index 000000000..148d4fc62 --- /dev/null +++ b/gtestsuite/src/test_addm.cpp @@ -0,0 +1,235 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_addm.h" + +// Local prototypes. +void libblis_test_addm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_addm_impl ( + iface_t iface, + obj_t* x, + obj_t* y +); + +double libblis_test_addm_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +); + +double libblis_ref_addm( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_addm_check( params, alpha, beta, x, y ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaddm_check( params, x, y, y_orig); + } + else { + resid = libblis_test_matrix_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_addm( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( y_orig, r ); + libblis_test_addm_impl( iface, x, r ); + resid = libblis_test_bitrp_matrix(y, r, dt); + } + return resid; +} + +double libblis_test_op_addm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +) { + num_t datatype; + dim_t m, n; + trans_t transx; + obj_t alpha, beta; + obj_t x, y, y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_trans( pc_str[0], &transx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transx, + sc_str[0], m, n, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &y_save ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Initialize alpha and beta. + bli_setsc( -1.0, -1.0, &alpha ); + bli_setsc( 3.0, 3.0, &beta ); + + // Randomize x. + bli_setm( &alpha, &x ); + bli_setm( &beta, &y ); + } + else { + libblis_test_mobj_irandomize( params, &x ); + libblis_test_mobj_irandomize( params, &y ); + } + + // Apply the parameters. + bli_obj_set_conjtrans( transx, &x ); + + //Copy c to c_save + bli_copym( &y, &y_save ); + + libblis_test_addm_impl( iface, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &r ); + resid = libblis_test_bitrp_addm( params, iface, &x, &y, &y_save, + &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_addm( params, &alpha, &beta, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_addm_impl( + iface_t iface, + obj_t* x, + obj_t* y +) { + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_addm( x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_addm_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + dim_t m = bli_obj_length( y ); + dim_t n = bli_obj_width( y ); + + conj_t conjx = bli_obj_conj_status( x ); + + obj_t aplusb; + obj_t alpha_conj; + obj_t norm_r, m_r, n_r, temp_r; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is set to alpha. + // - y_orig is set to beta. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig + conjx(x) + // + // is functioning correctly if + // + // normfm(y) - sqrt( absqsc( beta + conjx(alpha) ) * m * n ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt, &aplusb ); + bli_obj_scalar_init_detached( dt_real, &temp_r ); + bli_obj_scalar_init_detached( dt_real, &norm_r ); + bli_obj_scalar_init_detached( dt_real, &m_r ); + bli_obj_scalar_init_detached( dt_real, &n_r ); + + bli_obj_scalar_init_detached_copy_of( dt, conjx, alpha, &alpha_conj ); + + bli_normfm( y, &norm_r ); + + bli_copysc( beta, &aplusb ); + bli_addsc( &alpha_conj, &aplusb ); + + bli_setsc( ( double )m, 0.0, &m_r ); + bli_setsc( ( double )n, 0.0, &n_r ); + + bli_absqsc( &aplusb, &temp_r ); + bli_mulsc( &m_r, &temp_r ); + bli_mulsc( &n_r, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + bli_subsc( &temp_r, &norm_r ); + + bli_getsc( &norm_r, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_addm.h b/gtestsuite/src/test_addm.h new file mode 100644 index 000000000..1eafed499 --- /dev/null +++ b/gtestsuite/src/test_addm.h @@ -0,0 +1,16 @@ +#ifndef TEST_ADDM_H +#define TEST_ADDM_H + +#include "blis_test.h" + +double libblis_test_iaddm_check + ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_addm( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_ADDM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_addv.cpp b/gtestsuite/src/test_addv.cpp new file mode 100644 index 000000000..51a9e0d60 --- /dev/null +++ b/gtestsuite/src/test_addv.cpp @@ -0,0 +1,216 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_addv.h" + +// Local prototypes. +void libblis_test_addv_deps(thread_data_t* tdata, + test_params_t* params, test_op_t* op ); + +void libblis_test_addv_impl (iface_t iface, obj_t* x, obj_t* y); + +double libblis_test_addv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +); + +double libblis_ref_addv( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_addv_check(params, alpha, beta, x, y ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaddv_check(params, alpha, beta, x, y, y_save); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_addv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_addv_impl( iface, x, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_addv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t alpha, beta; + obj_t x, y, y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + // Initialize alpha and beta. + bli_setsc( -1.0, -1.0, &alpha ); + bli_setsc( 3.0, 3.0, &beta ); + + // Set x and y to alpha and beta, respectively. + bli_setv( &alpha, &x ); + bli_setv( &beta, &y ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &y, &y_save ); + + libblis_test_addv_impl( iface, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_addv( params, iface, &x, &y, &y_save, + &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_addv( params, &alpha, &beta, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_addv_impl ( + iface_t iface, + obj_t* x, + obj_t* y +){ + + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_addv( x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_addv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +) { + num_t dt = bli_obj_dt( x ); + num_t dt_real = bli_obj_dt_proj_to_real( x ); + dim_t m = bli_obj_vector_dim( x ); + + conj_t conjx = bli_obj_conj_status( x ); + + obj_t aplusb; + obj_t alpha_conj; + obj_t norm_r, m_r, temp_r; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is set to alpha. + // - y_orig is set to beta. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig + conjx(x) + // + // is functioning correctly if + // + // normfv(y) - sqrt( absqsc( beta + conjx(alpha) ) * m ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt, &aplusb ); + bli_obj_scalar_init_detached( dt_real, &temp_r ); + bli_obj_scalar_init_detached( dt_real, &norm_r ); + bli_obj_scalar_init_detached( dt_real, &m_r ); + + bli_obj_scalar_init_detached_copy_of( dt, conjx, alpha, &alpha_conj ); + + bli_normfv( y, &norm_r ); + + bli_copysc( beta, &aplusb ); + bli_addsc( &alpha_conj, &aplusb ); + + bli_setsc( ( double )m, 0.0, &m_r ); + + bli_absqsc( &aplusb, &temp_r ); + bli_mulsc( &m_r, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + bli_subsc( &temp_r, &norm_r ); + + bli_getsc( &norm_r, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_addv.h b/gtestsuite/src/test_addv.h new file mode 100644 index 000000000..173fb5a5c --- /dev/null +++ b/gtestsuite/src/test_addv.h @@ -0,0 +1,18 @@ +#ifndef TEST_ADDV_H +#define TEST_ADDV_H + +#include "blis_test.h" + +double libblis_test_iaddv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_addv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_ADDV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_amaxv.cpp b/gtestsuite/src/test_amaxv.cpp new file mode 100644 index 000000000..26b29309c --- /dev/null +++ b/gtestsuite/src/test_amaxv.cpp @@ -0,0 +1,477 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_amaxv.h" + +// Local prototypes. +void libblis_test_amaxv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_amaxv_impl ( + iface_t iface, + obj_t* x, + obj_t* index +); + +double libblis_test_amaxv_check ( + test_params_t* params, + obj_t* x, + obj_t* index +); + +void bli_amaxv_test ( + obj_t* x, + obj_t* index +); + +double cblas_amaxv( + f77_int m, + obj_t* x, + f77_int incx, + gint_t* idx, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* xp = (float*) bli_obj_buffer( x ); + *idx = cblas_isamax( m, xp, incx ); + break; + } + case BLIS_DOUBLE : + { + double* xp = (double*) bli_obj_buffer( x ); + *idx = cblas_idamax( m, xp, incx ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + *idx = cblas_icamax( m, xp, incx ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + *idx = cblas_izamax( m, xp, incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_amaxv( + f77_int m, + obj_t* x, + f77_int incx, + gint_t* idx, + num_t dt +){ + gint_t index = 1; + switch( dt ) { + case BLIS_FLOAT : + { + float* xp = (float*) bli_obj_buffer( x ); + index = isamax_( &m, xp, &incx ); + break; + } + case BLIS_DOUBLE : + { + double* xp = (double*) bli_obj_buffer( x ); + index = idamax_( &m, xp, &incx ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + index = icamax_( &m, xp, &incx ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + index = izamax_( &m, xp, &incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + *idx = (index - 1); + return 0; +} + +void libblis_api_amaxv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* index, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_amaxv_impl( iface, x, index ); + } + else { /*CLBAS || BLAS */ + dim_t m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + gint_t *idx = (gint_t *)bli_obj_buffer( index ); + + if( params->api == API_CBLAS ) { + cblas_amaxv( m, x, incx, idx, dt ); + } else { + blas_amaxv( m, x, incx, idx, dt );; + } + } + return ; +} + +double libblis_ref_amaxv( + test_params_t* params, + obj_t* x, + obj_t* index +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_amaxv_check( params, x, index ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iamaxv_check( params, x, index ); + } + else { + resid = libblis_test_vector_check(params, x); + } + } + + return resid; +} + +double libblis_test_bitrp_amaxv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* index, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_obj_scalar_init_detached( BLIS_INT, r ); + libblis_test_amaxv_impl( iface, x, r ); + resid = libblis_test_bitrp_vector(index, r, dt); + } + return resid; +} + +double libblis_test_op_amaxv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m; + obj_t x; + obj_t index; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Create test scalars. + bli_obj_scalar_init_detached( BLIS_INT, &index ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + + // Randomize x. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + } else { + libblis_test_vobj_irandomize( params, &x ); + } + + libblis_api_amaxv( params, iface, &x, &index, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + resid = libblis_test_bitrp_amaxv( params, iface, &x, &index, &r, datatype ); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_amaxv( params, &x, &index ); + } +#endif + + // Zero out performance and residual if input vector is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_amaxv_impl ( + iface_t iface, + obj_t* x, + obj_t* index +) { + + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_amaxv( x, index ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_amaxv_check ( + test_params_t* params, + obj_t* x, + obj_t* index +) { + obj_t index_test; + obj_t chi_i; + obj_t chi_i_test; + dim_t i; + dim_t i_test; + + double i_d, junk; + double i_d_test; + + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // + // Under these conditions, we assume that the implementation for + // + // index := amaxv( x ) + // + // is functioning correctly if + // + // x[ index ] = max( x ) + // + // where max() is implemented via the bli_?amaxv_test() function. + // + + // The following two calls have already been made by the caller. That + // is, the index object has already been created and the library's + // amaxv implementation has already been tested. + //bli_obj_scalar_init_detached( BLIS_INT, &index ); + //bli_amaxv( x, &index ); + bli_getsc( index, &i_d, &junk ); i = i_d; + + // If x is length 0, then we can't access any elements, and so we + // return early with a good residual. + if ( bli_obj_vector_dim( x ) == 0 ) { resid = 0.0; return resid; } + + bli_acquire_vi( i, x, &chi_i ); + + bli_obj_scalar_init_detached( BLIS_INT, &index_test ); + bli_amaxv_test( x, &index_test ); + bli_getsc( &index_test, &i_d_test, &junk ); i_test = i_d_test; + bli_acquire_vi( i_test, x, &chi_i_test ); + + // Verify that the values referenced by index and index_test are equal. + if ( bli_obj_equals( &chi_i, &chi_i_test ) ) resid = 0.0; + else resid = 1.0; + + return resid; +} + +// ----------------------------------------------------------------------------- + +// +// Prototype BLAS-like interfaces with typed operands for a local amaxv test +// operation +// + +#undef GENTPROT +#define GENTPROT( ctype, ch, opname ) \ +\ +void PASTEMAC(ch,opname) \ + ( \ + dim_t n, \ + ctype* restrict x, inc_t incx, \ + dim_t* restrict index \ + ); \ + +INSERT_GENTPROT_BASIC0( amaxv_test ) + + +// +// Prototype function pointer query interface. +// + +#undef GENPROT +#define GENPROT( tname, opname ) \ +\ +PASTECH(tname,_vft) \ +PASTEMAC(opname,_qfp)( num_t dt ); + +GENPROT( amaxv, amaxv_test ) + + +// +// Define function pointer query interfaces. +// + +#undef GENFRONT +#define GENFRONT( tname, opname ) \ +\ +GENARRAY_FPA( PASTECH(tname,_vft), \ + opname ); \ +\ +PASTECH(tname,_vft) \ +PASTEMAC(opname,_qfp)( num_t dt ) \ +{ \ + return PASTECH(opname,_fpa)[ dt ]; \ +} + +GENFRONT( amaxv, amaxv_test ) + + +// +// Define object-based interface for a local amaxv test operation. +// + +#undef GENFRONT +#define GENFRONT( tname, opname ) \ +\ +void PASTEMAC0(opname) \ + ( \ + obj_t* x, \ + obj_t* index \ + ) \ +{ \ + num_t dt = bli_obj_dt( x ); \ +\ + dim_t n = bli_obj_vector_dim( x ); \ + void* buf_x = bli_obj_buffer_at_off( x ); \ + inc_t incx = bli_obj_vector_inc( x ); \ +\ + dim_t* buf_index = (dim_t*)bli_obj_buffer_at_off( index ); \ +\ +/* + FGVZ: Disabling this code since bli_amaxv_check() is supposed to be a + non-public API function, and therefore unavailable unless all symbols + are scheduled to be exported at configure-time (which is not currently + the default behavior). + + if ( bli_error_checking_is_enabled() ) \ + bli_amaxv_check( x, index ); \ +*/ \ +\ + /* Query a type-specific function pointer, except one that uses + void* for function arguments instead of typed pointers. */ \ + PASTECH(tname,_vft) f = \ + PASTEMAC(opname,_qfp)( dt ); \ +\ + f \ + ( \ + n, \ + buf_x, incx, \ + buf_index \ + ); \ +} + +GENFRONT( amaxv, amaxv_test ) + + +// +// Define BLAS-like interfaces with typed operands for a local amaxv test +// operation. +// NOTE: This is based on a simplified version of the bli_?amaxv_ref() +// reference kernel. +// + +#undef GENTFUNCR +#define GENTFUNCR( ctype, ctype_r, ch, chr, varname ) \ +\ +void PASTEMAC(ch,varname) \ + ( \ + dim_t n, \ + ctype* x, inc_t incx, \ + dim_t* index \ + ) \ +{ \ + ctype_r* minus_one = PASTEMAC(chr,m1); \ + dim_t* zero_i = PASTEMAC(i,0); \ +\ + ctype_r chi1_r; \ + ctype_r chi1_i; \ + ctype_r abs_chi1; \ + ctype_r abs_chi1_max; \ + dim_t index_l; \ + dim_t i; \ +\ + /* If the vector length is zero, return early. This directly emulates + the behavior of netlib BLAS's i?amax() routines. */ \ + if ( bli_zero_dim1( n ) ) \ + { \ + PASTEMAC(i,copys)( *zero_i, *index ); \ + return; \ + } \ +\ + /* Initialize the index of the maximum absolute value to zero. */ \ + PASTEMAC(i,copys)( *zero_i, index_l ); \ +\ + /* Initialize the maximum absolute value search candidate with + -1, which is guaranteed to be less than all values we will + compute. */ \ + PASTEMAC(chr,copys)( *minus_one, abs_chi1_max ); \ +\ + { \ + for ( i = 0; i < n; ++i ) \ + { \ + ctype* chi1 = x + (i )*incx; \ +\ + /* Get the real and imaginary components of chi1. */ \ + PASTEMAC2(ch,chr,gets)( *chi1, chi1_r, chi1_i ); \ +\ + /* Replace chi1_r and chi1_i with their absolute values. */ \ + PASTEMAC(chr,abval2s)( chi1_r, chi1_r ); \ + PASTEMAC(chr,abval2s)( chi1_i, chi1_i ); \ +\ + /* Add the real and imaginary absolute values together. */ \ + PASTEMAC(chr,set0s)( abs_chi1 ); \ + PASTEMAC(chr,adds)( chi1_r, abs_chi1 ); \ + PASTEMAC(chr,adds)( chi1_i, abs_chi1 ); \ +\ + /* If the absolute value of the current element exceeds that of + the previous largest, save it and its index. If NaN is + encountered, then treat it the same as if it were a valid + value that was smaller than any previously seen. This + behavior mimics that of LAPACK's ?lange(). */ \ + if ( abs_chi1_max < abs_chi1 || bli_isnan( abs_chi1 ) ) \ + { \ + abs_chi1_max = abs_chi1; \ + index_l = i; \ + } \ + } \ + } \ +\ + /* Store the final index to the output variable. */ \ + PASTEMAC(i,copys)( index_l, *index ); \ +} +INSERT_GENTFUNCR_BASIC0( amaxv_test ) \ No newline at end of file diff --git a/gtestsuite/src/test_amaxv.h b/gtestsuite/src/test_amaxv.h new file mode 100644 index 000000000..043168492 --- /dev/null +++ b/gtestsuite/src/test_amaxv.h @@ -0,0 +1,15 @@ +#ifndef TEST_AMAXV_H +#define TEST_AMAXV_H + +#include "blis_test.h" + +double libblis_test_iamaxv_check + ( + test_params_t* params, + obj_t* x, + obj_t* index + ); + +double libblis_check_nan_amaxv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_AMAXV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_axpbyv.cpp b/gtestsuite/src/test_axpbyv.cpp new file mode 100644 index 000000000..e7d220e89 --- /dev/null +++ b/gtestsuite/src/test_axpbyv.cpp @@ -0,0 +1,380 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpbyv.h" + +// Local prototypes. +void libblis_test_axpbyv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_axpbyv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y +); + +double libblis_test_axpbyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +double cblas_axpbyv( + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* betap = (float*) bli_obj_buffer( beta ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_saxpby( m, *alphap, xp, incx, *betap, yp, incy ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* betap = (double*) bli_obj_buffer( beta ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_daxpby( m, *alphap, xp, incx, *betap, yp, incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_caxpby( m, alphap, xp, incx, betap, yp, incy ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zaxpby( m, alphap, xp, incx, betap, yp, incy );; + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_axpbyv( + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* betap = (float*) bli_obj_buffer( beta ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + saxpby_( &m, alphap, xp, &incx, betap, yp, &incy ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* betap = (double*) bli_obj_buffer( beta ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + daxpby_( &m, alphap, xp, &incx, betap, yp, &incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + caxpby_( &m, alphap, xp, &incx, betap, yp, &incy );; + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zaxpby_( &m, alphap, xp, &incx, betap, yp, &incy );; + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_axpbyv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_axpbyv_impl( iface, alpha, x, beta, y ); + } + else { /*CLBAS || BLAS */ + dim_t m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + + if(bli_obj_has_conj(x)) { + conjugate_tensor(x, dt); + bli_obj_set_conj( BLIS_NO_CONJUGATE, x ); + } + + if( params->api == API_CBLAS ) { + cblas_axpbyv( m, alpha, x, incx, beta, y, incy, dt ); + } else { + blas_axpbyv( m, alpha, x, incx, beta, y, incy, dt ); + } + } + return ; +} + +double libblis_ref_axpbyv( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_axpbyv_check( params, alpha, x, beta, y, y_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaxpbyv_check( params, alpha, x, beta, y, y_save ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_axpbyv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_axpbyv_impl( iface, alpha, x, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_axpbyv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t alpha, beta, x, y; + obj_t y_save; + double resid = 0.0; + obj_t xx; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[0], m, &xx ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &alpha ); + else + bli_setsc( 0.0, -2.0, &alpha ); + + bli_setsc( -1.0, 0.0, &beta ); + + // Randomize x and y, and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + } else { + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &x, &xx ); + + libblis_api_axpbyv( params, iface, &alpha, &xx, &beta, &y, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_axpbyv( params, iface, &alpha, &x, + &beta, &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_axpbyv( params, &alpha, &x, &beta, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_axpbyv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_axpbyv( alpha, x, beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_axpbyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t x_temp, y_temp; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + alpha * conjx(x) + // + // is functioning correctly if + // + // normfv( y - ( beta * y_orig + alpha * conjx(x) ) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &x_temp ); + bli_obj_create( dt, m, 1, 0, 0, &y_temp ); + + bli_copyv( x, &x_temp ); + bli_copyv( y_orig, &y_temp ); + + bli_scalv( alpha, &x_temp ); + bli_scalv( beta, &y_temp ); + bli_addv( &x_temp, &y_temp ); + + bli_subv( &y_temp, y ); + bli_normfv( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_axpbyv.h b/gtestsuite/src/test_axpbyv.h new file mode 100644 index 000000000..98dbb0a93 --- /dev/null +++ b/gtestsuite/src/test_axpbyv.h @@ -0,0 +1,18 @@ +#ifndef TEST_AXPBYV_H +#define TEST_AXPBYV_H + +#include "blis_test.h" + +double libblis_test_iaxpbyv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_axpbyv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_AXPBYV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_axpy2v.cpp b/gtestsuite/src/test_axpy2v.cpp new file mode 100644 index 000000000..456a60965 --- /dev/null +++ b/gtestsuite/src/test_axpy2v.cpp @@ -0,0 +1,274 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpy2v.h" + +// Local prototypes. +void libblis_test_axpy2v_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_axpy2v_impl ( + iface_t iface, + obj_t* alpha1, + obj_t* alpha2, + obj_t* x, + obj_t* y, + obj_t* z, + cntx_t* cntx +); + +double libblis_test_axpy2v_check ( + test_params_t* params, + obj_t* alpha1, + obj_t* alpha2, + obj_t* x, + obj_t* y, + obj_t* z, + obj_t* z_orig +); + +double libblis_ref_axpy2v ( + test_params_t* params, + obj_t* alpha1, + obj_t* alpha2, + obj_t* x, + obj_t* y, + obj_t* z, + obj_t* z_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_axpy2v_check( params, alpha1, alpha2, x, y, z, z_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaxpy2v_check( params, alpha1, alpha2, x, y, z, z_orig ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_axpy2v( + test_params_t* params, + iface_t iface, + obj_t* alpha1, + obj_t* alpha2, + obj_t* x, + obj_t* y, + obj_t* z, + cntx_t* cntx, + obj_t* z_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( z_orig, r ); + libblis_test_axpy2v_impl( iface, alpha1, alpha2, x, y, r, cntx); + resid = libblis_test_bitrp_vector(z, r, dt); + } + return resid; +} + +double libblis_test_op_axpy2v ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m; + conj_t conjx, conjy; + obj_t alpha1, alpha2, x, y, z; + obj_t z_save; + cntx_t* cntx; + double resid = 0.0; + + // Query a context. + cntx = bli_gks_query_cntx(); + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha1 ); + bli_obj_scalar_init_detached( datatype, &alpha2 ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[2], m, &z ); + libblis_test_vobj_create( params, datatype, sc_str[2], m, &z_save ); + + // Randomize x and y, and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Set alpha. + if ( bli_obj_is_real( &z ) ) { + bli_setsc( alpv.real, 0.0, &alpha1 ); + bli_setsc( betv.real, 0.0, &alpha2 ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha1 ); + bli_setsc( betv.real, (betv.real/1.2), &alpha2 ); + } + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + libblis_test_vobj_randomize( params, TRUE, &z ); + } else { + int32_t xx = (int32_t)alpv.real; + int32_t yy = (int32_t)betv.real; + if ( bli_obj_is_real( &z ) ) { + bli_setsc( (double)xx, 0.0, &alpha1 ); + bli_setsc( (double)yy, 0.0, &alpha2 ); + } + else { + int32_t ac = (int32_t)(xx/0.8); + int32_t bc = (int32_t)(yy/1.0); + bli_setsc( (double)xx, (double)ac, &alpha1 ); + bli_setsc( (double)yy, (double)bc, &alpha2 ); + } + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + libblis_test_vobj_irandomize( params, &z ); + } + + bli_copyv( &z, &z_save ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + libblis_test_axpy2v_impl( iface, &alpha1, &alpha2, &x, &y, &z, cntx); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], m, &r ); + + resid = libblis_test_bitrp_axpy2v( params, iface, &alpha1, + &alpha2, &x, &y, &z, cntx, &z_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_axpy2v( params, &alpha1, &alpha2, &x, &y, &z, &z_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &z, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &z ); + libblis_test_obj_free( &z_save ); + + return abs(resid); +} + +void libblis_test_axpy2v_impl ( + iface_t iface, + obj_t* alpha1, + obj_t* alpha2, + obj_t* x, + obj_t* y, + obj_t* z, + cntx_t* cntx +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_axpy2v_ex( alpha1, alpha2, x, y, z, cntx, NULL ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_axpy2v_check ( + test_params_t* params, + obj_t* alpha1, + obj_t* alpha2, + obj_t* x, + obj_t* y, + obj_t* z, + obj_t* z_orig +) { + num_t dt = bli_obj_dt( z ); + num_t dt_real = bli_obj_dt_proj_to_real( z ); + + dim_t m = bli_obj_vector_dim( z ); + + obj_t x_temp, y_temp, z_temp; + obj_t norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // - y is randomized. + // - z_orig is randomized. + // Note: + // - alpha1, alpha2 should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // z := z_orig + alpha1 * conjx(x) + alpha2 * conjy(y) + // + // is functioning correctly if + // + // normfv( z - v ) + // + // is negligible, where v contains z as computed by two calls to axpyv. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &x_temp ); + bli_obj_create( dt, m, 1, 0, 0, &y_temp ); + bli_obj_create( dt, m, 1, 0, 0, &z_temp ); + + bli_copyv( x, &x_temp ); + bli_copyv( y, &y_temp ); + bli_copyv( z_orig, &z_temp ); + + bli_scalv( alpha1, &x_temp ); + bli_scalv( alpha2, &y_temp ); + bli_addv( &x_temp, &z_temp ); + bli_addv( &y_temp, &z_temp ); + + bli_subv( &z_temp, z ); + bli_normfv( z, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + bli_obj_free( &z_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_axpy2v.h b/gtestsuite/src/test_axpy2v.h new file mode 100644 index 000000000..4400a5c68 --- /dev/null +++ b/gtestsuite/src/test_axpy2v.h @@ -0,0 +1,19 @@ +#ifndef TEST_AXPY2V_H +#define TEST_AXPY2V_H + +#include "blis_test.h" + +double libblis_test_iaxpy2v_check + ( + test_params_t* params, + obj_t* alphax, + obj_t* alphay, + obj_t* x, + obj_t* y, + obj_t* z, + obj_t* z_orig + ); + +double libblis_check_nan_axpy2v( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_AXPY2V_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_axpyf.cpp b/gtestsuite/src/test_axpyf.cpp new file mode 100644 index 000000000..90fcf4127 --- /dev/null +++ b/gtestsuite/src/test_axpyf.cpp @@ -0,0 +1,268 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpyf.h" + +// Local prototypes. +void libblis_test_axpyf_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_axpyf_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + cntx_t* cntx +); + +double libblis_test_axpyf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + obj_t* y_orig +); + +double libblis_ref_axpyf ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_axpyf_check( params, alpha, a, x, y, y_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaxpyf_check( params, alpha, a, x, y, y_orig ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_axpyf( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + cntx_t* cntx, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_axpyf_impl( iface, alpha, a, x, r, cntx ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_axpyf ( + test_params_t* params, + test_op_t* op, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +) { + num_t datatype; + dim_t m, b_n; + conj_t conja, conjx; + obj_t alpha, a, x, y; + obj_t y_save; + cntx_t* cntx; + double resid = 0.0; + + // Query a context. + cntx = bli_gks_query_cntx(); + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Query the operation's fusing factor for the current datatype. + b_n = bli_cntx_get_blksz_def_dt( datatype, BLIS_AF, cntx ); + + // Store the fusing factor so that the driver can retrieve the value + // later when printing results. + op->dim_aux[0] = b_n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conja ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, b_n, &a ); + libblis_test_vobj_create( params, datatype, sc_str[1], b_n, &x ); + libblis_test_vobj_create( params, datatype, sc_str[2], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[2], m, &y_save ); + + // Set alpha. + if ( bli_obj_is_real( &y ) ) { + bli_setsc( -1.0, 0.0, &alpha ); + } + else { + bli_setsc( 0.0, -1.0, &alpha ); + } + + // Randomize A, x, and y, and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_mobj_randomize( params, FALSE, &a ); + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + } else { + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conja, &a ); + bli_obj_set_conj( conjx, &x ); + + libblis_test_axpyf_impl( iface, &alpha, &a, &x, &y, cntx ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], m, &r ); + + resid = libblis_test_bitrp_axpyf( params, iface, &alpha, &a, + &x, &y, cntx, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_axpyf( params, &alpha, &a, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_axpyf_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + cntx_t* cntx +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_axpyf_ex( alpha, a, x, y, cntx, NULL ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_axpyf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + dim_t b_n = bli_obj_width( a ); + + dim_t i; + + obj_t a1, chi1, v; + obj_t alpha_chi1; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized. + // - x is randomized. + // - y is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig + alpha * conja(A) * conjx(x) + // + // is functioning correctly if + // + // normfv( y - v ) + // + // is negligible, where v contains y as computed by repeated calls to + // axpyv. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + bli_obj_scalar_init_detached( dt, &alpha_chi1 ); + + bli_obj_create( dt, m, 1, 0, 0, &v ); + + bli_copyv( y_orig, &v ); + + for ( i = 0; i < b_n; ++i ) { + bli_acquire_mpart_l2r( BLIS_SUBPART1, i, 1, a, &a1 ); + bli_acquire_vpart_f2b( BLIS_SUBPART1, i, 1, x, &chi1 ); + + bli_copysc( &chi1, &alpha_chi1 ); + bli_mulsc( alpha, &alpha_chi1 ); + + bli_axpyv( &alpha_chi1, &a1, &v ); + } + + bli_subv( y, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &v ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_axpyf.h b/gtestsuite/src/test_axpyf.h new file mode 100644 index 000000000..6b7532143 --- /dev/null +++ b/gtestsuite/src/test_axpyf.h @@ -0,0 +1,18 @@ +#ifndef TEST_AXPYF_H +#define TEST_AXPYF_H + +#include "blis_test.h" + +double libblis_test_iaxpyf_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_axpyf( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_AXPYF_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_axpym.cpp b/gtestsuite/src/test_axpym.cpp new file mode 100644 index 000000000..e48749c82 --- /dev/null +++ b/gtestsuite/src/test_axpym.cpp @@ -0,0 +1,232 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpym.h" + +// Local prototypes. +void libblis_test_axpym_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_axpym_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +); + +double libblis_test_axpym_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_save +); + +double libblis_ref_axpym( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_axpym_check( params, alpha, x, y, y_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaxpym_check( params, alpha, x, y, y_orig); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_axpym( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( y_orig, r ); + libblis_test_axpym_impl( iface, alpha, x, r ); + resid = libblis_test_bitrp_matrix(y, r, dt); + } + return resid; +} + +double libblis_test_op_axpym ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m, n; + trans_t transx; + obj_t alpha, x, y; + obj_t y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_trans( pc_str[0], &transx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transx, + sc_str[0], m, n, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y_save ); + + // Set alpha. + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &alpha ); + else + bli_setsc( 0.0, -2.0, &alpha ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Randomize and save y. + libblis_test_mobj_randomize( params, FALSE, &x ); + libblis_test_mobj_randomize( params, FALSE, &y ); + } + else { + libblis_test_mobj_irandomize( params, &x ); + libblis_test_mobj_irandomize( params, &y ); + } + + bli_copym( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conjtrans( transx, &x ); + + libblis_test_axpym_impl( iface, &alpha, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + + resid = libblis_test_bitrp_axpym( params, iface, &alpha, &x, + &y, &y_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_axpym( params, &alpha, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_axpym_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_axpym( alpha, x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_axpym_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_length( y ); + dim_t n = bli_obj_width( y ); + + obj_t x_temp, y_temp; + obj_t norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig + alpha * conjx(x) + // + // is functioning correctly if + // + // normfm( y - ( y_orig + alpha * conjx(x) ) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, n, 0, 0, &x_temp ); + bli_obj_create( dt, m, n, 0, 0, &y_temp ); + + bli_copym( x, &x_temp ); + bli_copym( y_orig, &y_temp ); + + bli_scalm( alpha, &x_temp ); + bli_addm( &x_temp, &y_temp ); + + bli_subm( &y_temp, y ); + bli_normfm( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_axpym.h b/gtestsuite/src/test_axpym.h new file mode 100644 index 000000000..54d0bb3c2 --- /dev/null +++ b/gtestsuite/src/test_axpym.h @@ -0,0 +1,17 @@ +#ifndef TEST_AXPYM_H +#define TEST_AXPYM_H + +#include "blis_test.h" + +double libblis_test_iaxpym_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_axpym( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_AXPYM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_axpyv.cpp b/gtestsuite/src/test_axpyv.cpp new file mode 100644 index 000000000..dc68abdaa --- /dev/null +++ b/gtestsuite/src/test_axpyv.cpp @@ -0,0 +1,357 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_axpyv.h" + +// Local prototypes. +void libblis_test_axpyv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_axpyv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +); + +double libblis_test_axpyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +); + +double cblas_axpyv( + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_saxpy( m, *alphap, xp, incx, yp, incy ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_daxpy( m, *alphap, xp, incx, yp, incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_caxpy( m, alphap, xp, incx, yp, incy ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zaxpy( m, alphap, xp, incx, yp, incy ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_axpyv( + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + saxpy_( &m, alphap, xp, &incx, yp, &incy ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + daxpy_( &m, alphap, xp, &incx, yp, &incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + caxpy_( &m, alphap, xp, &incx, yp, &incy ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zaxpy_( &m, alphap, xp, &incx, yp, &incy ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_axpyv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_axpyv_impl( iface, alpha, x, y ); + } + else { /*CLBAS || BLAS */ + dim_t m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + + if(bli_obj_has_conj(x)) { + conjugate_tensor(x, dt); + bli_obj_set_conj( BLIS_NO_CONJUGATE, x ); + } + + if( params->api == API_CBLAS ) { + cblas_axpyv( m, alpha, x, incx, y, incy, dt ); + } else { + blas_axpyv( m, alpha, x, incx, y, incy, dt ); + } + } + return ; +} + +double libblis_ref_axpyv( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_axpyv_check( params, alpha, x, y, y_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iaxpyv_check( params, alpha, x, y, y_save ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_axpyv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_save, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_save, r ); + libblis_test_axpyv_impl( iface, alpha, x, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_axpyv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t alpha, x, y; + obj_t y_save; + double resid = 0.0; + obj_t xx; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[0], m, &xx ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + // Set alpha. + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &alpha ); + else + bli_setsc( 0.0, -2.0, &alpha ); + + // Randomize x and y, and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + } else { + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &x, &xx ); + + libblis_api_axpyv( params, iface, &alpha, &xx, &y, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_axpyv( params, iface, &alpha, &x, + &y, &y_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_axpyv( params, &alpha, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_axpyv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_axpyv( alpha, x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_axpyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t x_temp, y_temp; + obj_t norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig + alpha * conjx(x) + // + // is functioning correctly if + // + // normfv( y - ( y_orig + alpha * conjx(x) ) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &x_temp ); + bli_obj_create( dt, m, 1, 0, 0, &y_temp ); + + bli_copyv( x, &x_temp ); + bli_copyv( y_orig, &y_temp ); + + bli_scalv( alpha, &x_temp ); + bli_addv( &x_temp, &y_temp ); + + bli_subv( &y_temp, y ); + bli_normfv( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_axpyv.h b/gtestsuite/src/test_axpyv.h new file mode 100644 index 000000000..783fd83a4 --- /dev/null +++ b/gtestsuite/src/test_axpyv.h @@ -0,0 +1,17 @@ +#ifndef TEST_AXPYV_H +#define TEST_AXPYV_H + +#include "blis_test.h" + +double libblis_test_iaxpyv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_axpyv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_AXPYV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_copym.cpp b/gtestsuite/src/test_copym.cpp new file mode 100644 index 000000000..b06e320a2 --- /dev/null +++ b/gtestsuite/src/test_copym.cpp @@ -0,0 +1,189 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_copym.h" + +// Local prototypes. +void libblis_test_copym_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_copym_impl ( + iface_t iface, + obj_t* x, + obj_t* y +); + +double libblis_test_copym_check ( + test_params_t* params, + obj_t* x, + obj_t* y +); + +double libblis_ref_copym( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_copym_check( params, x, y ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_icopym_check( params, x, y, y_save ); + } + else { + resid = libblis_test_matrix_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_copym( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_setm( &BLIS_ONE, r ); + libblis_test_copym_impl( iface, x, r ); + resid = libblis_test_bitrp_matrix(y, r, dt); + } + return resid; +} + +double libblis_test_op_copym ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m, n; + trans_t transx; + obj_t x, y, y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_trans( pc_str[0], &transx ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transx, + sc_str[0], m, n, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &y_save ); + + // Randomize x and set y to one. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_mobj_randomize( params, FALSE, &x ); + } + else { + libblis_test_mobj_irandomize( params, &x ); + } + + bli_setm( &BLIS_ONE, &y ); + + // Apply the parameters. + bli_obj_set_conjtrans( transx, &x ); + + bli_copym( &y, &y_save ); + + libblis_test_copym_impl( iface, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + resid = libblis_test_bitrp_copym( params, iface, &x, &y, &y_save, datatype); + } + else { + resid = libblis_ref_copym( params, &x, &y, &y_save); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_copym_impl( + iface_t iface, + obj_t* x, + obj_t* y +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_copym( x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_copym_check( + test_params_t* params, + obj_t* x, + obj_t* y +) { + num_t dt_real = bli_obj_dt_proj_to_real( x ); + + obj_t norm_y_r; + + double junk; + + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // + // Under these conditions, we assume that the implementation for + // + // y := conjx(x) + // + // is functioning correctly if + // + // normfm( y - conjx(x) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm_y_r ); + + bli_subm( x, y ); + + bli_normfm( y, &norm_y_r ); + + bli_getsc( &norm_y_r, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_copym.h b/gtestsuite/src/test_copym.h new file mode 100644 index 000000000..ac970f4fd --- /dev/null +++ b/gtestsuite/src/test_copym.h @@ -0,0 +1,16 @@ +#ifndef TEST_COPYM_H +#define TEST_COPYM_H + +#include "blis_test.h" + +double libblis_test_icopym_check + ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_copym( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_COPYM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_copyv.cpp b/gtestsuite/src/test_copyv.cpp new file mode 100644 index 000000000..238165530 --- /dev/null +++ b/gtestsuite/src/test_copyv.cpp @@ -0,0 +1,306 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_copyv.h" + +// Local prototypes. +void libblis_test_copyv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_copyv_impl ( + iface_t iface, + obj_t* x, + obj_t* y +); + +double libblis_test_copyv_check ( + test_params_t* params, + obj_t* x, + obj_t* y +); + +double cblas_copyv( + f77_int m, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_scopy( m, xp, incx, yp, incy ); + break; + } + case BLIS_DOUBLE : + { + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dcopy( m, xp, incx, yp, incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_ccopy( m, xp, incx, yp, incy ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zcopy( m, xp, incx, yp, incy ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_copyv( + f77_int m, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + scopy_( &m, xp, &incx, yp, &incy ); + break; + } + case BLIS_DOUBLE : + { + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + dcopy_( &m, xp, &incx, yp, &incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + ccopy_( &m, xp, &incx, yp, &incy ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zcopy_( &m, xp, &incx, yp, &incy ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_copyv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_copyv_impl( iface, x, y ); + } + else { /*CLBAS || BLAS */ + dim_t m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + + if(bli_obj_has_conj(x)) { + conjugate_tensor(x, dt); + bli_obj_set_conj( BLIS_NO_CONJUGATE, x ); + } + + if( params->api == API_CBLAS ) { + cblas_copyv( m, x, incx, y, incy, dt ); + } else { + blas_copyv( m, x, incx, y, incy, dt ); + } + } + return ; +} + +double libblis_ref_copyv( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_copyv_check( params, x, y ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_icopyv_check( params, x, y, y_save ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_copyv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_setv( &BLIS_ONE, r ); + libblis_test_copyv_impl( iface, x, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_copyv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t x, y, y_save; + double resid = 0.0; + obj_t xx; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[0], m, &xx ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + + // Randomize x and set y to one. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + } + else { + libblis_test_vobj_irandomize( params, &x ); + } + + bli_setv( &BLIS_ONE, &y ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &y, &y_save ); + + bli_copyv( &x, &xx ); + + libblis_api_copyv( params, iface, &xx, &y, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + resid = libblis_test_bitrp_copyv( params, iface, &x, &y, &y_save, datatype); + } + else { + resid = libblis_ref_copyv( params, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_copyv_impl ( + iface_t iface, + obj_t* x, + obj_t* y +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_copym( x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_copyv_check ( + test_params_t* params, + obj_t* x, + obj_t* y +) { + num_t dt_real = bli_obj_dt_proj_to_real( x ); + + obj_t norm_y_r; + + double junk; + + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // + // Under these conditions, we assume that the implementation for + // + // y := conjx(x) + // + // is functioning correctly if + // + // normfv( y - conjx(x) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm_y_r ); + + bli_subv( x, y ); + + bli_normfv( y, &norm_y_r ); + + bli_getsc( &norm_y_r, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_copyv.h b/gtestsuite/src/test_copyv.h new file mode 100644 index 000000000..6ba8f163a --- /dev/null +++ b/gtestsuite/src/test_copyv.h @@ -0,0 +1,16 @@ +#ifndef TEST_COPYV_H +#define TEST_COPYV_H + +#include "blis_test.h" + +double libblis_test_icopyv_check + ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_copyv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_COPYV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_dotaxpyv.cpp b/gtestsuite/src/test_dotaxpyv.cpp new file mode 100644 index 000000000..71c31709b --- /dev/null +++ b/gtestsuite/src/test_dotaxpyv.cpp @@ -0,0 +1,306 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotaxpyv.h" + +// Local prototypes. +void libblis_test_dotaxpyv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_dotaxpyv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* z, + cntx_t* cntx +); + +double libblis_test_dotaxpyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* z, + obj_t* z_orig +); + +double libblis_ref_dotaxpyv ( + test_params_t* params, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* z, + obj_t* z_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_dotaxpyv_check( params, alpha, xt, x, y, rho, z, z_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_idotaxpyv_check( params, alpha, xt, x, y, rho, z, z_orig ); + } + else { + resid = libblis_test_vector_check(params, z); + } + } + return resid; +} + +double libblis_test_bitrp_dotxaxpyf( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* z, + obj_t* z_orig, + cntx_t* cntx, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + obj_t rh; + + for(i = 0; i < n_repeats; i++) { + bli_copysc( &BLIS_MINUS_ONE, &rh ); + bli_copyv( z_orig, r ); + libblis_test_dotaxpyv_impl( iface, alpha, xt, x, y, rho, r, cntx ); + resid = libblis_test_bitrp_vector(&rh, rho, dt); + resid += libblis_test_bitrp_vector(z, r, dt); + } + + return resid; +} + +double libblis_test_op_dotaxpyv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +) { + num_t datatype; + dim_t m; + conj_t conjxt, conjx, conjy; + conj_t conjconjxty; + obj_t alpha, xt, x, y, rho, z; + obj_t z_save; + cntx_t* cntx; + double resid = 0.0; + + // Query a context. + cntx = bli_gks_query_cntx(); + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjxt ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[2], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &rho ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[2], m, &z ); + libblis_test_vobj_create( params, datatype, sc_str[2], m, &z_save ); + + // Randomize x and z, and save z. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Set alpha. + if ( bli_obj_is_real( &z ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + } + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &z ); + } else { + int32_t xx = (int32_t)alpv.real; + if ( bli_obj_is_real( &z ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + } + else { + int32_t ac = (int32_t)(xx/0.8); + bli_setsc( (double)xx, (double)ac, &alpha ); + } + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &z ); + } + + bli_copyv( &z, &z_save ); + + // Create an alias to x for xt. (Note that it doesn't actually need to be + // transposed.) + bli_obj_alias_to( &x, &xt ); + + // Determine whether to make a copy of x with or without conjugation. + // + // conjx conjy ~conjx^conjy y is initialized as + // n n c y = conj(x) + // n c n y = x + // c n n y = x + // c c c y = conj(x) + // + conjconjxty = bli_apply_conj( conjxt, conjy ); + conjconjxty = bli_conj_toggled( conjconjxty ); + bli_obj_set_conj( conjconjxty, &xt ); + bli_copyv( &xt, &y ); + + // Apply the parameters. + bli_obj_set_conj( conjxt, &xt ); + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + libblis_test_dotaxpyv_impl( iface, &alpha, &xt, &x, &y, &rho, &z, cntx ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], m, &r ); + + resid = libblis_test_bitrp_dotxaxpyf(params, iface, &alpha, + &xt, &x, &y, &rho, &z, &z_save, cntx, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_dotaxpyv( params, &alpha, &xt, + &x, &y, &rho, &z, &z_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &z, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &z ); + libblis_test_obj_free( &z_save ); + + return abs(resid); +} + +void libblis_test_dotaxpyv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* z, + cntx_t* cntx +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_dotaxpyv_ex( alpha, xt, x, y, rho, z, cntx, NULL ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_dotaxpyv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* z, + obj_t* z_orig +) { + num_t dt = bli_obj_dt( z ); + num_t dt_real = bli_obj_dt_proj_to_real( z ); + + dim_t m = bli_obj_vector_dim( z ); + + obj_t rho_temp; + + obj_t z_temp; + obj_t norm_z; + + double resid1, resid2; + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y is randomized. + // - z_orig is randomized. + // - xt is an alias to x. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // rho := conjxt(x^T) conjy(y) + // z := z_orig + alpha * conjx(x) + // + // is functioning correctly if + // + // ( rho - rho_temp ) + // + // and + // + // normfv( z - z_temp ) + // + // are negligible, where rho_temp and z_temp contain rho and z as + // computed by dotv and axpyv, respectively. + // + + bli_obj_scalar_init_detached( dt, &rho_temp ); + bli_obj_scalar_init_detached( dt_real, &norm_z ); + + bli_obj_create( dt, m, 1, 0, 0, &z_temp ); + bli_copyv( z_orig, &z_temp ); + + + bli_dotv( xt, y, &rho_temp ); + bli_axpyv( alpha, x, &z_temp ); + + + bli_subsc( rho, &rho_temp ); + bli_getsc( &rho_temp, &resid1, &junk ); + + bli_subv( &z_temp, z ); + bli_normfv( z, &norm_z ); + bli_getsc( &norm_z, &resid2, &junk ); + + resid = bli_fmaxabs( resid1, resid2 ); + + bli_obj_free( &z_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_dotaxpyv.h b/gtestsuite/src/test_dotaxpyv.h new file mode 100644 index 000000000..f6cb66832 --- /dev/null +++ b/gtestsuite/src/test_dotaxpyv.h @@ -0,0 +1,20 @@ +#ifndef TEST_DOTAXPYV_H +#define TEST_DOTAXPYV_H + +#include "blis_test.h" + +double libblis_test_idotaxpyv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* xt, + obj_t* x, + obj_t* y, + obj_t* rho_orig, + obj_t* z, + obj_t* z_orig + ); + +double libblis_check_nan_dotaxpyv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_DOTAXPYV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_dotv.cpp b/gtestsuite/src/test_dotv.cpp new file mode 100644 index 000000000..d35153289 --- /dev/null +++ b/gtestsuite/src/test_dotv.cpp @@ -0,0 +1,369 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotv.h" + +// Local prototypes. +void libblis_test_dotv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_dotv_impl ( + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* rho +); + +double libblis_test_dotv_check ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* rho +); + +double cblas_dotv( + f77_int m, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* rho, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + float* resp = (float*) bli_obj_buffer( rho ); + *resp = cblas_sdot( m, xp, incx, yp, incy ); + break; + } + case BLIS_DOUBLE : + { + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + double* resp = (double*) bli_obj_buffer( rho ); + *resp = cblas_ddot( m, xp, incx, yp, incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + scomplex* resp = (scomplex*) bli_obj_buffer( rho ); + cblas_cdotu_sub( m, xp, incx, yp, incy, resp ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + dcomplex* resp = (dcomplex*) bli_obj_buffer( rho ); + cblas_zdotu_sub( m, xp, incx, yp, incy, resp ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_dotv( + f77_int m, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* rho, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + float* resp = (float*) bli_obj_buffer( rho ); + *resp = sdot_( &m, xp, &incx, yp, &incy ); + break; + } + case BLIS_DOUBLE : + { + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + double* resp = (double*) bli_obj_buffer( rho ); + *resp = ddot_( &m, xp, &incx, yp, &incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + scomplex* resp = (scomplex*) bli_obj_buffer( rho ); +#ifdef BLIS_DISABLE_COMPLEX_RETURN_INTEL + *resp = cdotu_( &m, xp, &incx, yp, &incy ); +#else + cdotu_( resp, &m, xp, &incx, yp, &incy ); +#endif // BLIS_DISABLE_COMPLEX_RETURN_INTEL ... + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + dcomplex* resp = (dcomplex*) bli_obj_buffer( rho ); +#ifdef BLIS_DISABLE_COMPLEX_RETURN_INTEL + *resp = zdotu_( &m, xp, &incx, yp, &incy ); +#else + zdotu_( resp, &m, xp, &incx, yp, &incy ); +#endif // BLIS_DISABLE_COMPLEX_RETURN_INTEL ... + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_dotv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* rho, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_dotv_impl( iface, x, y, rho ); + } + else { /*CLBAS || BLAS */ + dim_t m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + + if(bli_obj_has_conj(x)) { + conjugate_tensor(x, dt); + bli_obj_set_conj( BLIS_NO_CONJUGATE, x ); + } + + if(bli_obj_has_conj(y)) { + conjugate_tensor(y, dt); + bli_obj_set_conj( BLIS_NO_CONJUGATE, y ); + } + + if( params->api == API_CBLAS ) { + cblas_dotv( m, x, incx, y, incy, rho, dt ); + } else { + blas_dotv( m, x, incx, y, incy, rho, dt ); + } + } + return ; +} + +double libblis_ref_dotv( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* rho +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_dotv_check( params, x, y, rho ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_idotv_check( params, x, y, rho ); + } + else { + resid = libblis_test_vector_check(params, rho); + } + } + return resid; +} + +double libblis_test_bitrp_dotv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* rho, + obj_t* rh, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copysc( &BLIS_MINUS_ONE, rh ); + libblis_test_dotv_impl( iface, x, y, rh ); + resid = libblis_test_bitrp_vector(rho, rh, dt); + } + return resid; +} + +double libblis_test_op_dotv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m; + conj_t conjx, conjy, conjconjxy; + obj_t x, y, rho; + double resid = 0.0; + obj_t xx, yy; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &rho ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[0], m, &xx ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &yy ); + + // Randomize x. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, TRUE, &x ); + } else { + libblis_test_vobj_irandomize( params, &x ); + } + + // Determine whether to make a copy of x with or without conjugation. + // conjx conjy ~conjx^conjy y is initialized as + // n n c y = conj(x) + // n c n y = x + // c n n y = x + // c c c y = conj(x) + + conjconjxy = bli_apply_conj( conjx, conjy ); + conjconjxy = bli_conj_toggled( conjconjxy ); + bli_obj_set_conj( conjconjxy, &x ); + bli_copyv( &x, &y ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + bli_copysc( &BLIS_MINUS_ONE, &rho ); + + bli_copyv( &x, &xx ); + bli_copyv( &y, &yy ); + + libblis_api_dotv( params, iface, &xx, &yy, &rho, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + bli_obj_scalar_init_detached( datatype, &r ); + + resid = libblis_test_bitrp_dotv( params, iface, &x, &y, &rho, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_dotv( params, &x, &y, &rho ); + } +#endif + + // Zero out performance and residual if output scalar is empty. + libblis_test_check_empty_problem( &rho, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &yy ); + + return abs(resid); +} + +void libblis_test_dotv_impl ( + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* rho +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_dotv( x, y, rho ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_dotv_check ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* rho +){ + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + obj_t rho_r, rho_i; + obj_t norm_x, norm_xy; + + double zero; + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y is equal to conj(conjx(conjy(x))). + // + // Under these conditions, we assume that the implementation for + // + // rho := conjx(x^T) conjy(y) + // + // is functioning correctly if + // + // sqrtsc( rho.real ) - normfv( x ) + // + // and + // + // rho.imag + // + // are negligible. + // + + bli_obj_scalar_init_detached( dt_real, &rho_r ); + bli_obj_scalar_init_detached( dt_real, &rho_i ); + bli_obj_scalar_init_detached( dt_real, &norm_x ); + bli_obj_scalar_init_detached( dt_real, &norm_xy ); + + bli_normfv( x, &norm_x ); + + bli_unzipsc( rho, &rho_r, &rho_i ); + + bli_sqrtsc( &rho_r, &norm_xy ); + + bli_subsc( &norm_x, &norm_xy ); + bli_getsc( &norm_xy, &resid, &junk ); + bli_getsc( &rho_i, &zero, &junk ); + + resid = bli_fmaxabs( resid, zero ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_dotv.h b/gtestsuite/src/test_dotv.h new file mode 100644 index 000000000..93849d020 --- /dev/null +++ b/gtestsuite/src/test_dotv.h @@ -0,0 +1,16 @@ +#ifndef TEST_DOTV_H +#define TEST_DOTV_H + +#include "blis_test.h" + +double libblis_test_idotv_check + ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* rho + ); + +double libblis_check_nan_dotv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_DOTV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_dotxaxpyf.cpp b/gtestsuite/src/test_dotxaxpyf.cpp new file mode 100644 index 000000000..598c6d7ae --- /dev/null +++ b/gtestsuite/src/test_dotxaxpyf.cpp @@ -0,0 +1,371 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotxaxpyf.h" + +// Local prototypes. +void libblis_test_dotxaxpyf_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_dotxaxpyf_impl ( + iface_t iface, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + cntx_t* cntx +); + +double libblis_test_dotxaxpyf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + obj_t* y_orig, + obj_t* z_orig +); + +double libblis_ref_dotxaxpyf ( + test_params_t* params, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + obj_t* y_orig, + obj_t* z_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_dotxaxpyf_check( params, alpha, at, a, w, x, + beta, y, z, y_orig, z_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_idotxaxpyf_check( params, alpha, at, a, w, x, + beta, y, z, y_orig, z_orig); + } + else { + resid = libblis_test_vector_check(params, y); + resid = libblis_test_vector_check(params, z); + } + } + return resid; +} + +double libblis_test_bitrp_dotxaxpyf( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + cntx_t* cntx, + obj_t* y_orig, + obj_t* z_orig, + obj_t* r, + obj_t* v, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + bli_copyv( z_orig, v ); + libblis_test_dotxaxpyf_impl( iface, alpha, at, a, w, x, + beta, r, v, cntx ); + resid = libblis_test_bitrp_vector(y, r, dt); + resid += libblis_test_bitrp_vector(z, v, dt); + } + return resid; +} + +double libblis_test_op_dotxaxpyf ( + test_params_t* params, + test_op_t* op, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, b_n; + conj_t conjat, conja, conjw, conjx; + obj_t alpha, at, a, w, x, beta, y, z; + obj_t y_save, z_save; + cntx_t* cntx; + double resid = 0.0; + + // Query a context. + cntx = bli_gks_query_cntx(); + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Query the operation's fusing factor for the current datatype. + b_n = bli_cntx_get_blksz_def_dt( datatype, BLIS_XF, cntx ); + + // Store the fusing factor so that the driver can retrieve the value + // later when printing results. + op->dim_aux[0] = b_n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjat ); + bli_param_map_char_to_blis_conj( pc_str[1], &conja ); + bli_param_map_char_to_blis_conj( pc_str[2], &conjw ); + bli_param_map_char_to_blis_conj( pc_str[3], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, b_n, &a ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &w ); + libblis_test_vobj_create( params, datatype, sc_str[2], b_n, &x ); + libblis_test_vobj_create( params, datatype, sc_str[3], b_n, &y ); + libblis_test_vobj_create( params, datatype, sc_str[3], b_n, &y_save ); + libblis_test_vobj_create( params, datatype, sc_str[4], m, &z ); + libblis_test_vobj_create( params, datatype, sc_str[4], m, &z_save ); + + // Randomize A, w, x, y, and z, and save y and z. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Set alpha. + if ( bli_obj_is_real( &y ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, (betv.real/1.2), &beta ); + } + libblis_test_mobj_randomize( params, FALSE, &a ); + libblis_test_vobj_randomize( params, FALSE, &w ); + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + libblis_test_vobj_randomize( params, FALSE, &z ); + } else { + int32_t xx = (int32_t)alpv.real; + int32_t yy = (int32_t)betv.real; + if ( bli_obj_is_real( &z ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + bli_setsc( (double)yy, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(xx/0.8); + int32_t bc = (int32_t)(yy/1.0); + bli_setsc( (double)xx, (double)ac, &alpha ); + bli_setsc( (double)yy, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &w ); + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + libblis_test_vobj_irandomize( params, &z ); + } + + bli_copyv( &y, &y_save ); + bli_copyv( &z, &z_save ); + + // Create an alias to a for at. (Note that it should NOT actually be + // marked for transposition since the transposition is part of the dotxf + // subproblem.) + bli_obj_alias_to( &a, &at ); + + // Apply the parameters. + bli_obj_set_conj( conjat, &at ); + bli_obj_set_conj( conja, &a ); + bli_obj_set_conj( conjw, &w ); + bli_obj_set_conj( conjx, &x ); + + libblis_test_dotxaxpyf_impl( iface, &alpha, &at, &a, &w, &x, + &beta, &y, &z, cntx ); +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r,v; + + libblis_test_vobj_create( params, datatype, sc_str[3], b_n, &r ); + + libblis_test_vobj_create( params, datatype, sc_str[4], m, &v ); + + resid = libblis_test_bitrp_dotxaxpyf(params, iface, &alpha, &at, &a, &w, + &x, &beta, &y, &z, cntx, &y_save, &z_save, &r, &v, datatype); + + bli_obj_free( &r ); + bli_obj_free( &v ); + } + else { + resid = libblis_ref_dotxaxpyf( params, &alpha, &at, &a, &w, &x, + &beta, &y, &z, &y_save, &z_save); + } +#endif + + // Zero out performance and residual if either output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + libblis_test_check_empty_problem( &z, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &w ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &z ); + libblis_test_obj_free( &y_save ); + libblis_test_obj_free( &z_save ); + + return abs(resid); +} + +void libblis_test_dotxaxpyf_impl ( + iface_t iface, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + cntx_t* cntx +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_dotxaxpyf_ex( alpha, at, a, w, x, beta, y, z, cntx, NULL ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_dotxaxpyf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + obj_t* y_orig, + obj_t* z_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( z ); + dim_t b_n = bli_obj_vector_dim( y ); + + dim_t i; + + obj_t a1, chi1, psi1, v, q; + obj_t alpha_chi1; + obj_t norm; + + double resid1, resid2; + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized. + // - w is randomized. + // - x is randomized. + // - y is randomized. + // - z is randomized. + // - at is an alias to a. + // Note: + // - alpha and beta should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + alpha * conjat(A^T) * conjw(w) + // z := z_orig + alpha * conja(A) * conjx(x) + // + // is functioning correctly if + // + // normfv( y - v ) + // + // and + // + // normfv( z - q ) + // + // are negligible, where v and q contain y and z as computed by repeated + // calls to dotxv and axpyv, respectively. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + bli_obj_scalar_init_detached( dt, &alpha_chi1 ); + + bli_obj_create( dt, b_n, 1, 0, 0, &v ); + bli_obj_create( dt, m, 1, 0, 0, &q ); + + bli_copyv( y_orig, &v ); + bli_copyv( z_orig, &q ); + + // v := beta * v + alpha * conjat(at) * conjw(w) + for ( i = 0; i < b_n; ++i ) { + bli_acquire_mpart_l2r( BLIS_SUBPART1, i, 1, at, &a1 ); + bli_acquire_vpart_f2b( BLIS_SUBPART1, i, 1, &v, &psi1 ); + + bli_dotxv( alpha, &a1, w, beta, &psi1 ); + } + + // q := q + alpha * conja(a) * conjx(x) + for ( i = 0; i < b_n; ++i ) { + bli_acquire_mpart_l2r( BLIS_SUBPART1, i, 1, a, &a1 ); + bli_acquire_vpart_f2b( BLIS_SUBPART1, i, 1, x, &chi1 ); + + bli_copysc( &chi1, &alpha_chi1 ); + bli_mulsc( alpha, &alpha_chi1 ); + + bli_axpyv( &alpha_chi1, &a1, &q ); + } + + + bli_subv( y, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid1, &junk ); + + bli_subv( z, &q ); + bli_normfv( &q, &norm ); + bli_getsc( &norm, &resid2, &junk ); + + + resid = bli_fmaxabs( resid1, resid2 ); + + bli_obj_free( &v ); + bli_obj_free( &q ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_dotxaxpyf.h b/gtestsuite/src/test_dotxaxpyf.h new file mode 100644 index 000000000..397899bf2 --- /dev/null +++ b/gtestsuite/src/test_dotxaxpyf.h @@ -0,0 +1,23 @@ +#ifndef TEST_DOTXAXPYF_H +#define TEST_DOTXAXPYF_H + +#include "blis_test.h" + +double libblis_test_idotxaxpyf_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* at, + obj_t* a, + obj_t* w, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* z, + obj_t* y_orig, + obj_t* z_orig + ); + +double libblis_check_nan_dotxaxpyf( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_DOTXAXPYF_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_dotxf.cpp b/gtestsuite/src/test_dotxf.cpp new file mode 100644 index 000000000..db286b920 --- /dev/null +++ b/gtestsuite/src/test_dotxf.cpp @@ -0,0 +1,291 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotxf.h" + +// Local prototypes. +void libblis_test_dotxf_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_dotxf_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + cntx_t* cntx +); + +double libblis_test_dotxf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +double libblis_ref_dotxf ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_dotxf_check( params, alpha, a, x, beta, y, y_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_idotxf_check( params, alpha, a, x, beta, y, y_orig ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + + +double libblis_test_bitrp_dotxf( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + cntx_t* cntx, + obj_t* r, + num_t dt +){ + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_dotxf_impl( iface, alpha, a, x, beta, r, cntx ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_dotxf ( + test_params_t* params, + test_op_t* op, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, b_n; + conj_t conjat, conjx; + obj_t alpha, a, x, beta, y; + obj_t y_save; + + cntx_t* cntx; + double resid = 0.0; + + // Query a context. + cntx = bli_gks_query_cntx(); + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Query the operation's fusing factor for the current datatype. + b_n = bli_cntx_get_blksz_def_dt( datatype, BLIS_DF, cntx ); + + // Store the fusing factor so that the driver can retrieve the value + // later when printing results. + op->dim_aux[0] = b_n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjat ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, b_n, &a ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[2], b_n, &y ); + libblis_test_vobj_create( params, datatype, sc_str[2], b_n, &y_save ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Set alpha. + if ( bli_obj_is_real( &y ) ) { + bli_setsc( 1.2, 0.0, &alpha ); + bli_setsc( -1.0, 0.0, &beta ); + } + else { + bli_setsc( 1.2, 0.1, &alpha ); + bli_setsc( -1.0, -0.1, &beta ); + } + + // Randomize A, x, and y, and save y. + libblis_test_mobj_randomize( params, FALSE, &a ); + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + } + else { + int32_t xx = (int32_t) 1.0; + int32_t yy = (int32_t)-1.0; + if ( bli_obj_is_real( &y ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + bli_setsc( (double)yy, 0.0, &beta ); + } + else { + // For syrk, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + int32_t ac = (int32_t)(xx/0.8); + int32_t bc = (int32_t)(yy/1.0); + bli_setsc( (double)xx, (double)ac, &alpha ); + bli_setsc( (double)yy, (double)bc, &beta ); + } + + // Randomize A, x, and y, and save y. + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjat, &a ); + bli_obj_set_conj( conjx, &x ); + + libblis_test_dotxf_impl( iface, &alpha, &a, &x, &beta, &y, cntx ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], b_n, &r ); + + resid = libblis_test_bitrp_dotxf( params, iface, &alpha, &a, &x, + &beta, &y, &y_save, cntx, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_dotxf( params, &alpha, &a, &x, &beta, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_dotxf_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + cntx_t* cntx +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_dotxf_ex( alpha, a, x, beta, y, cntx, NULL ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_dotxf_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t b_n = bli_obj_vector_dim( y ); + + dim_t i; + + obj_t a1, psi1, v; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized. + // - x is randomized. + // - y is randomized. + // Note: + // - alpha and beta should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + alpha * conjat(A^T) * conjx(x) + // + // is functioning correctly if + // + // normfv( y - v ) + // + // is negligible, where v contains y as computed by repeated calls to + // dotxv. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, b_n, 1, 0, 0, &v ); + + bli_copyv( y_orig, &v ); + + for ( i = 0; i < b_n; ++i ) { + bli_acquire_mpart_l2r( BLIS_SUBPART1, i, 1, a, &a1 ); + bli_acquire_vpart_f2b( BLIS_SUBPART1, i, 1, &v, &psi1 ); + + bli_dotxv( alpha, &a1, x, beta, &psi1 ); + } + + bli_subv( y, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &v ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_dotxf.h b/gtestsuite/src/test_dotxf.h new file mode 100644 index 000000000..b6a69f65d --- /dev/null +++ b/gtestsuite/src/test_dotxf.h @@ -0,0 +1,19 @@ +#ifndef TEST_DOTXF_H +#define TEST_DOTXF_H + +#include "blis_test.h" + +double libblis_test_idotxf_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_dotxf( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_DOTXF_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_dotxv.cpp b/gtestsuite/src/test_dotxv.cpp new file mode 100644 index 000000000..f0bc316e1 --- /dev/null +++ b/gtestsuite/src/test_dotxv.cpp @@ -0,0 +1,262 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_dotxv.h" + +// Local prototypes. +void libblis_test_dotxv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_dotxv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho +); + +double libblis_test_dotxv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho, + obj_t* rho_orig +); + +double libblis_ref_dotxv( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho, + obj_t* rho_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_dotxv_check( params, alpha, x, y, beta, rho, + rho_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_idotxv_check( params, alpha, x, y, beta, rho, rho_orig ); + } + else { + resid = libblis_test_vector_check(params, rho); + } + } + return resid; +} + +double libblis_test_bitrp_dotxv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho, + obj_t* rho_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copysc( rho_orig, r ); + libblis_test_dotxv_impl( iface, alpha, x, y, beta, r ); + resid = libblis_test_bitrp_vector(rho, r, dt); + } + return resid; +} + +double libblis_test_op_dotxv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m; + conj_t conjx, conjy, conjconjxy; + obj_t alpha, x, y, beta, rho, rho_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + bli_obj_scalar_init_detached( datatype, &rho ); + bli_obj_scalar_init_detached( datatype, &rho_save ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + + // Initialize alpha, beta, and rho. + bli_copysc( &BLIS_ONE, &alpha ); + bli_copysc( &BLIS_ZERO, &beta ); + bli_copysc( &BLIS_MINUS_ONE, &rho ); + bli_copysc( &rho, &rho_save ); + + // Randomize x. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, TRUE, &x ); + } else { + libblis_test_vobj_irandomize( params, &x ); + } + + // Determine whether to make a copy of x with or without conjugation. + // + // conjx conjy ~conjx^conjy y is initialized as + // n n c y = conj(x) + // n c n y = x + // c n n y = x + // c c c y = conj(x) + // + conjconjxy = bli_apply_conj( conjx, conjy ); + conjconjxy = bli_conj_toggled( conjconjxy ); + bli_obj_set_conj( conjconjxy, &x ); + bli_copyv( &x, &y ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + libblis_test_dotxv_impl( iface, &alpha, &x, &y, &beta, &rho ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + bli_copysc( &rho, &r ); + + resid = libblis_test_bitrp_dotxv( params, iface, &alpha, + &x, &y, &beta, &rho, &rho_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_dotxv( params, &alpha, &x, &y, &beta, &rho, &rho_save ); + } +#endif + + // Zero out performance and residual if output scalar is empty. + libblis_test_check_empty_problem( &rho, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + + return abs(resid); +} + +void libblis_test_dotxv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_dotxv( alpha, x, y, beta, rho ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + + + +double libblis_test_dotxv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho, + obj_t* rho_orig +) { + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + obj_t rho_r, rho_i; + obj_t norm_x_r, norm_xy_r; + obj_t temp_r; + + double zero; + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // - y is equal to conjx(conjy(x)). + // - alpha must be real-valued. + // - beta must be zero. + // Note: + // - We forgo fully exercising beta scaling in order to simplify the + // test. + // + // Under these conditions, we assume that the implementation for + // + // rho := beta * rho_orig + alpha * conjx(x^T) conjy(y) + // + // is functioning correctly if + // + // sqrtsc( rho.real ) - sqrtsc( alpha ) * normfv( x ) + // + // and + // + // rho.imag + // + // are negligible. + // + + bli_obj_scalar_init_detached( dt_real, &rho_r ); + bli_obj_scalar_init_detached( dt_real, &rho_i ); + bli_obj_scalar_init_detached( dt_real, &norm_x_r ); + bli_obj_scalar_init_detached( dt_real, &norm_xy_r ); + bli_obj_scalar_init_detached( dt_real, &temp_r ); + + bli_copysc( alpha, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + + bli_normfv( x, &norm_x_r ); + bli_mulsc( &temp_r, &norm_x_r ); + + bli_unzipsc( rho, &rho_r, &rho_i ); + + bli_sqrtsc( &rho_r, &norm_xy_r ); + + bli_subsc( &norm_x_r, &norm_xy_r ); + bli_getsc( &norm_xy_r, &resid, &junk ); + bli_getsc( &rho_i, &zero, &junk ); + + resid = bli_fmaxabs( resid, zero ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_dotxv.h b/gtestsuite/src/test_dotxv.h new file mode 100644 index 000000000..c62c39326 --- /dev/null +++ b/gtestsuite/src/test_dotxv.h @@ -0,0 +1,19 @@ +#ifndef TEST_DOTXV_H +#define TEST_DOTXV_H + +#include "blis_test.h" + +double libblis_test_idotxv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* beta, + obj_t* rho, + obj_t* rho_orig + ); + +double libblis_check_nan_dotxv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_DOTXV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_gemm.cpp b/gtestsuite/src/test_gemm.cpp new file mode 100644 index 000000000..88e1a160a --- /dev/null +++ b/gtestsuite/src/test_gemm.cpp @@ -0,0 +1,556 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_gemm.h" + +using namespace std; + +// Local prototypes. +void libblis_test_gemm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_gemm_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +); + +double libblis_test_gemm_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_gemm( + test_params_t* params, + f77_int m, + f77_int n, + f77_int k, + f77_int lda, + f77_int ldb, + f77_int ldc, + obj_t* a, + obj_t* b, + obj_t* c, + obj_t* alpha, + obj_t* beta, + num_t dt, + trans_t transa, + trans_t transb +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_TRANSPOSE cblas_transa; + enum CBLAS_TRANSPOSE cblas_transb; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_trans( transa ) ) + cblas_transa = CblasTrans; + else if( bli_is_conjtrans( transa ) ) + cblas_transa = CblasConjTrans; + else + cblas_transa = CblasNoTrans; + + if( bli_is_trans( transb ) ) + cblas_transb = CblasTrans; + else if( bli_is_conjtrans( transb ) ) + cblas_transb = CblasConjTrans; + else + cblas_transb = CblasNoTrans; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_sgemm( cblas_order, cblas_transa, cblas_transb, + m, n, k, *alphap, ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dgemm( cblas_order, cblas_transa, cblas_transb, + m, n, k, *alphap, ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_cgemm( cblas_order, cblas_transa, cblas_transb, + m, n, k, alphap, ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zgemm( cblas_order, cblas_transa, cblas_transb, + m, n, k, alphap, ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return 0; +} + +double blas_gemm( + f77_int m, + f77_int n, + f77_int k, + f77_int lda, + f77_int ldb, + f77_int ldc, + obj_t* a, + obj_t* b, + obj_t* c, + obj_t* alpha, + obj_t* beta, + num_t dt, + f77_char f77_transa, + f77_char f77_transb +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + sgemm_( &f77_transa, &f77_transb, &m, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp,(f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dgemm_( &f77_transa, &f77_transb, &m, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp,(f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cgemm_( &f77_transa, &f77_transb, &m, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp,(f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zgemm_( &f77_transa, &f77_transb, &m, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp,(f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_gemm( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_gemm_impl( iface, alpha, a, b, beta, c ); + } + else { /*CLBAS || BLAS */ + + f77_int mm = bli_obj_length( c ); + f77_int kk = bli_obj_width_after_trans( a ); + f77_int nn = bli_obj_width( c ); + trans_t transa = bli_obj_conjtrans_status( a ); + trans_t transb = bli_obj_conjtrans_status( b ); + f77_int lda, ldb, ldc; + + if( bli_obj_row_stride( c ) == 1 ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + ldc = ldc + params->ld[2]; + } + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(bli_obj_has_notrans(b) && bli_obj_has_conj(b)) { + conjugate_tensor(b, dt); + transb = bli_obj_onlytrans_status( b ); + } + + if(params->api == API_CBLAS) { + cblas_gemm( params, mm, nn, kk, lda, ldb, ldc, a, b, c, + alpha, beta, dt, transa, transb); + } else { /**/ + f77_char f77_transa; + f77_char f77_transb; + if(transa == BLIS_TRANSPOSE) f77_transa='T'; + else if ( transa == BLIS_CONJ_TRANSPOSE ) f77_transa='C'; + else /*if ( transa == BLIS_NO_TRANSPOSE )*/ f77_transa='N'; + + if(transb == BLIS_TRANSPOSE) f77_transb='T'; + else if ( transb == BLIS_CONJ_TRANSPOSE ) f77_transb='C'; + else /*if ( transb == BLIS_NO_TRANSPOSE )*/ f77_transb='N'; + + if( bli_obj_row_stride( c ) == 1 ) { + blas_gemm(mm, nn, kk, lda, ldb, ldc, a, b, c, + alpha, beta, dt, f77_transa, f77_transb); + } + else { + blas_gemm(nn, mm, kk, ldb, lda, ldc, b, a, c, + alpha, beta, dt, f77_transb, f77_transa); + } + } + } + return ; +} + +double libblis_ref_gemm( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_save, + num_t dt +) { + + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_gemm(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_gemm_check( params, alpha, a, b, beta, c, c_save); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_igemm_check( params, alpha, a, b, beta, c, c_save, dt); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_gemm( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_gemm_impl( iface, alpha, a, b, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_gemm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m, n, k; + trans_t transa; + trans_t transb; + obj_t alpha, a, b, beta, c; + obj_t c_save; + double resid = 0.0; + obj_t aa, bb; + + // Use a different function to handle mixed datatypes. + if ( params->mixed_domain || params->mixed_precision ) { + // TODO : GtestSuite Not Implemented + return resid; + } + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + // Map parameter characters to BLIS constants. + if(params->api == API_BLIS) { + bli_param_map_char_to_blis_trans( pc_str[0], &transa ); + bli_param_map_char_to_blis_trans( pc_str[1], &transb ); + } else { + bli_param_map_char_to_blas_trans( pc_str[0], &transa ); + bli_param_map_char_to_blas_trans( pc_str[1], &transb ); + } + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], m, k, &a ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], m, k, &aa ); + libblis_test_mobj_create( params, datatype, transb, + sc_str[2], k, n, &b ); + libblis_test_mobj_create( params, datatype, transb, + sc_str[2], k, n, &bb ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c_save ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, (betv.real/1.2), &beta ); + } + // Randomize A, B, and C, and save C. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_mobj_randomize( params, TRUE, &b ); + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)alpv.real; + int32_t y = (int32_t)betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + libblis_test_mobj_irandomize( params, &c ); + } + + if ((params->nanf) && (betv.real == 0) ) { + test_fillbuffmem(&c, datatype ); + } + + //Copy c to c_save + bli_copym( &c, &c_save ); + + bli_copym( &a, &aa ); + bli_copym( &b, &bb ); + + // Apply the parameters. + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_conjtrans( transb, &b ); + + bli_obj_set_conjtrans( transa, &aa ); + bli_obj_set_conjtrans( transb, &bb ); + + libblis_api_gemm(params, iface, &alpha, &aa, &bb, &beta, &c, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + resid = libblis_test_bitrp_gemm( params, iface, &alpha, &a, &b, &beta, + &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_gemm(params, &alpha, &a, &b, &beta, + &c, &c_save, datatype ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &bb ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_gemm_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +){ + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_gemm( alpha, a, b, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_gemm_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +){ + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t n = bli_obj_width( c ); + dim_t k = bli_obj_width_after_trans( a ); + + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized. + // - b is randomized. + // - c_orig is randomized. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transb(B) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // z = ( beta * C_orig + alpha * transa(A) * transb(B) ) * t + // = beta * C_orig * t + alpha * transa(A) * transb(B) * t + // = beta * C_orig * t + alpha * transa(A) * w + // = beta * C_orig * t + z + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, k, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + bli_gemv( &BLIS_ONE, b, &t, &BLIS_ZERO, &w ); + bli_gemv( alpha, a, &w, &BLIS_ZERO, &z ); + bli_gemv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm.h b/gtestsuite/src/test_gemm.h new file mode 100644 index 000000000..e3bce7ae8 --- /dev/null +++ b/gtestsuite/src/test_gemm.h @@ -0,0 +1,20 @@ +#ifndef TEST_GEMM_H +#define TEST_GEMM_H + +#include "blis_test.h" + +double libblis_test_igemm_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + num_t dt + ); + +double libblis_check_nan_gemm(obj_t* c, num_t dt ); + +#endif /* TEST_GEMM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_bf16bf16f32obf16.cpp b/gtestsuite/src/test_gemm_bf16bf16f32obf16.cpp new file mode 100644 index 000000000..9f5ad9652 --- /dev/null +++ b/gtestsuite/src/test_gemm_bf16bf16f32obf16.cpp @@ -0,0 +1,262 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static bfloat16* aocl_reorder(bfloat16* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_bf16bf16f32of32('B',k, n); + bfloat16* b_reorder = ( bfloat16* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_bf16bf16f32of32('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_bf16bf16f32obf16( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + float alpha, + bfloat16* a, + dim_t lda, + char reordera, + bfloat16* b, + dim_t ldb, + char reorderb, + float beta, + bfloat16* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( bfloat16 ) * (m * n) ); + + aocl_gemm_bf16bf16f32obf16(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_bf16bf16f32obf16(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const float)alpha, (const bfloat16 *)a, (const dim_t)lda, (const char)reordera, (const bfloat16 *)b, + (const dim_t)ldb, (const char)reorderb, (const float)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_bf16bf16f32obf16( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + float alpha, + bfloat16* a, + dim_t lda, + bfloat16* b, + dim_t ldb, + float beta, + bfloat16* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_bf16bf16f32obf16(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + bfloat16* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_bf16bf16f32obf16(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_bf16bf16f32obf16 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + float alpha, + bfloat16* a, + dim_t lda, + bfloat16* b, + dim_t ldb, + float beta, + bfloat16* c, + dim_t ldc, + bfloat16* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver_bf16( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_bf16bf16f32obf16 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = true; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + if( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) { + cout << "Recorer not supported in column major" << endl; + return resid; + } + } + + /* Get 64 byte aligned memory.*/ + bfloat16* a = ( bfloat16* ) malloc( sizeof( bfloat16 ) * m * k ); + float *a_float = ( float* ) malloc( m * k * sizeof( float )); + for ( int32_t i = 0; i < m*k; ++i ) + { + a_float[i] = ( float ) ( i % 5 ); + } + convert_float_arr_to_bf16( a_float, a, m * k ); + + bfloat16* b = ( bfloat16* ) malloc( sizeof( bfloat16 ) * n * k ); + float *b_float = ( float* ) malloc( k * n * sizeof( float )); + for ( int32_t i = 0; i < k*n; ++i ) + { + b_float[i] = ( float ) ( i % 5 ); + } + convert_float_arr_to_bf16( b_float, b, k * n ); + + bfloat16* c = ( bfloat16* ) malloc( sizeof( bfloat16 ) * m * n ); + memset( ( void* ) c, 0, sizeof( bfloat16 ) * m * n ); + + bfloat16* c_ref = ( bfloat16* ) malloc( sizeof( bfloat16 ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( bfloat16 ) * m * n ); + + float alpha = 2 ; + float beta = 9 ; + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_bf16bf16f32obf16(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_bf16bf16f32obf16( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( a_float != NULL ) + free( a_float ); + if ( b != NULL ) + free( b ); + if ( b_float != NULL ) + free( b_float ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_bf16bf16f32of32.cpp b/gtestsuite/src/test_gemm_bf16bf16f32of32.cpp new file mode 100644 index 000000000..168924d29 --- /dev/null +++ b/gtestsuite/src/test_gemm_bf16bf16f32of32.cpp @@ -0,0 +1,262 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static bfloat16* aocl_reorder(bfloat16* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_bf16bf16f32of32('B',k, n); + bfloat16* b_reorder = ( bfloat16* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_bf16bf16f32of32('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_bf16bf16f32of32( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + float alpha, + bfloat16* a, + dim_t lda, + char reordera, + bfloat16* b, + dim_t ldb, + char reorderb, + float beta, + float* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( float ) * (m * n) ); + + aocl_gemm_bf16bf16f32of32(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_bf16bf16f32of32(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const float)alpha, (const bfloat16 *)a, (const dim_t)lda, (const char)reordera, (const bfloat16 *)b, + (const dim_t)ldb, (const char)reorderb, (const float)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_bf16bf16f32of32( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + float alpha, + bfloat16* a, + dim_t lda, + bfloat16* b, + dim_t ldb, + float beta, + float* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_bf16bf16f32of32(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + bfloat16* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_bf16bf16f32of32(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_bf16bf16f32of32 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + float alpha, + bfloat16* a, + dim_t lda, + bfloat16* b, + dim_t ldb, + float beta, + float* c, + dim_t ldc, + float* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver_bf16( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_bf16bf16f32of32 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = false; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + if( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) { + cout << "Recorer not supported in column major" << endl; + return resid; + } + } + + /* Get 64 byte aligned memory.*/ + bfloat16* a = ( bfloat16* ) malloc( sizeof( bfloat16 ) * m * k ); + float *a_float = (float *)malloc( m * k * sizeof( float )); + for ( int32_t i = 0; i < m*k; ++i ) + { + a_float[i] = ( float ) ( i % 5 ); + } + convert_float_arr_to_bf16( a_float, a, m * k ); + + bfloat16* b = ( bfloat16* ) malloc( sizeof( bfloat16 ) * n * k ); + float *b_float = (float *)malloc( k * n * sizeof( float )); + for ( int32_t i = 0; i < k*n; ++i ) + { + b_float[i] = ( float ) ( i % 5 ); + } + convert_float_arr_to_bf16( b_float, b, k * n ); + + float* c = ( float* ) malloc( sizeof( float ) * m * n ); + memset( ( void* ) c, 0, sizeof( float ) * m * n ); + + float* c_ref = ( float* ) malloc( sizeof( float ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( float ) * m * n ); + + float alpha = 2 ; + float beta = 9 ; + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_bf16bf16f32of32(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_bf16bf16f32of32( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( a_float != NULL ) + free( a_float ); + if ( b != NULL ) + free( b ); + if ( b_float != NULL ) + free( b_float ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_f32f32f32of32.cpp b/gtestsuite/src/test_gemm_f32f32f32of32.cpp new file mode 100644 index 000000000..9a176bfe2 --- /dev/null +++ b/gtestsuite/src/test_gemm_f32f32f32of32.cpp @@ -0,0 +1,248 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static float* aocl_reorder(float* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_f32f32f32of32('B',k, n); + float* b_reorder = ( float* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_f32f32f32of32('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_f32f32f32of32( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + float alpha, + float* a, + dim_t lda, + char reordera, + float* b, + dim_t ldb, + char reorderb, + float beta, + float* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( float ) * (m * n) ); + + aocl_gemm_f32f32f32of32(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_f32f32f32of32(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const float)alpha, (const float *)a, (const dim_t)lda, (const char)reordera, (const float *)b, + (const dim_t)ldb, (const char)reorderb, (const float)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_f32f32f32of32( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + float alpha, + float* a, + dim_t lda, + float* b, + dim_t ldb, + float beta, + float* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_f32f32f32of32(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + float* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_f32f32f32of32(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_f32f32f32of32 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + float alpha, + float* a, + dim_t lda, + float* b, + dim_t ldb, + float beta, + float* c, + dim_t ldc, + float* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_f32f32f32of32 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = false; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + cout << "Column Major not supported" << endl; + return resid; + } + + + /* Get 64 byte aligned memory.*/ + float* a = ( float* ) malloc( sizeof( float ) * m * k ); + + float* b = ( float* ) malloc( sizeof( float ) * n * k ); + + float* c = ( float* ) malloc( sizeof( float ) * m * n ); + memset( ( void* ) c, 0, sizeof( float ) * m * n ); + + float* c_ref = ( float* ) malloc( sizeof( float ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( float ) * m * n ); + + float alpha = 2; + float beta = 9 ; + + fill_array( a, ( m * k ) ); + fill_array( b, ( k * n ) ); + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_f32f32f32of32(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_f32f32f32of32( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( b != NULL ) + free( b ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_u8s8s16os16.cpp b/gtestsuite/src/test_gemm_u8s8s16os16.cpp new file mode 100644 index 000000000..2f61fc556 --- /dev/null +++ b/gtestsuite/src/test_gemm_u8s8s16os16.cpp @@ -0,0 +1,248 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static int8_t* aocl_reorder(int8_t* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_u8s8s16os16('B',k, n); + int8_t* b_reorder = ( int8_t* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_u8s8s16os16('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_u8s8s16os16( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int16_t alpha, + uint8_t* a, + dim_t lda, + char reordera, + int8_t* b, + dim_t ldb, + char reorderb, + int16_t beta, + int16_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( int16_t ) * (m * n) ); + + aocl_gemm_u8s8s16os16(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_u8s8s16os16(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const int8_t)alpha, (const uint8_t *)a, (const dim_t)lda, (const char)reordera, (const int8_t *)b, + (const dim_t)ldb, (const char)reorderb, (const int8_t)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_u8s8s16os16( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int16_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int16_t beta, + int16_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_u8s8s16os16(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + int8_t* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_u8s8s16os16(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_u8s8s16os16 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + int16_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int16_t beta, + int16_t* c, + dim_t ldc, + int16_t* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_u8s8s16os16 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = false; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + cout << "Column Major not supported" << endl; + return resid; + } + + + /* Get 64 byte aligned memory.*/ + uint8_t* a = ( uint8_t* ) malloc( sizeof( uint8_t ) * m * k ); + + int8_t* b = ( int8_t* ) malloc( sizeof( int8_t ) * n * k ); + + int16_t* c = ( int16_t* ) malloc( sizeof( int16_t ) * m * n ); + memset( ( void* ) c, 0, sizeof( int16_t ) * m * n ); + + int16_t* c_ref = ( int16_t* ) malloc( sizeof( int16_t ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( int16_t ) * m * n ); + + int16_t alpha = 2; + int16_t beta = 9; + + fill_array( a, ( m * k ) ); + fill_array( b, ( k * n ) ); + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_u8s8s16os16(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_u8s8s16os16( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( b != NULL ) + free( b ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_u8s8s16os8.cpp b/gtestsuite/src/test_gemm_u8s8s16os8.cpp new file mode 100644 index 000000000..e0090fbb1 --- /dev/null +++ b/gtestsuite/src/test_gemm_u8s8s16os8.cpp @@ -0,0 +1,248 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static int8_t* aocl_reorder(int8_t* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_u8s8s16os16('B',k, n); + int8_t* b_reorder = ( int8_t* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_u8s8s16os16('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_u8s8s16os8( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int8_t alpha, + uint8_t* a, + dim_t lda, + char reordera, + int8_t* b, + dim_t ldb, + char reorderb, + int8_t beta, + int8_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( int8_t ) * (m * n) ); + + aocl_gemm_u8s8s16os8(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_u8s8s16os8(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const int8_t)alpha, (const uint8_t *)a, (const dim_t)lda, (const char)reordera, (const int8_t *)b, + (const dim_t)ldb, (const char)reorderb, (const int8_t)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_u8s8s16os8( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int8_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int8_t beta, + int8_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_u8s8s16os8(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + int8_t* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_u8s8s16os8(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_u8s8s16os8 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + int8_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int8_t beta, + int8_t* c, + dim_t ldc, + int8_t* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_u8s8s16os8 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = true; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + cout << "Column Major not supported" << endl; + return resid; + } + + + /* Get 64 byte aligned memory.*/ + uint8_t* a = ( uint8_t* ) malloc( sizeof( uint8_t ) * m * k ); + + int8_t* b = ( int8_t* ) malloc( sizeof( int8_t ) * n * k ); + + int8_t* c = ( int8_t* ) malloc( sizeof( int8_t ) * m * n ); + memset( ( void* ) c, 0, sizeof( int8_t ) * m * n ); + + int8_t* c_ref = ( int8_t* ) malloc( sizeof( int8_t ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( int8_t ) * m * n ); + + int8_t alpha = 2; + int8_t beta = 9; + + fill_array( a, ( m * k ) ); + fill_array( b, ( k * n ) ); + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_u8s8s16os8(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_u8s8s16os8( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( b != NULL ) + free( b ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_u8s8s32os32.cpp b/gtestsuite/src/test_gemm_u8s8s32os32.cpp new file mode 100644 index 000000000..65ed46d97 --- /dev/null +++ b/gtestsuite/src/test_gemm_u8s8s32os32.cpp @@ -0,0 +1,248 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static int8_t* aocl_reorder(int8_t* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_u8s8s32os32('B',k, n); + int8_t* b_reorder = ( int8_t* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_u8s8s32os32('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_u8s8s32os32( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int32_t alpha, + uint8_t* a, + dim_t lda, + char reordera, + int8_t* b, + dim_t ldb, + char reorderb, + int32_t beta, + int32_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( int32_t ) * (m * n) ); + + aocl_gemm_u8s8s32os32(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_u8s8s32os32(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const int32_t)alpha, (const uint8_t *)a, (const dim_t)lda, (const char)reordera, (const int8_t *)b, + (const dim_t)ldb, (const char)reorderb, (const int32_t)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_u8s8s32os32( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int32_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int32_t beta, + int32_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_u8s8s32os32(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + int8_t* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_u8s8s32os32(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_u8s8s32os32 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + int32_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int32_t beta, + int32_t* c, + dim_t ldc, + int32_t* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_u8s8s32os32 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = false; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + cout << "Column Major not supported" << endl; + return resid; + } + + + /* Get 64 byte aligned memory.*/ + uint8_t* a = ( uint8_t* ) malloc( sizeof( uint8_t ) * m * k ); + + int8_t* b = ( int8_t* ) malloc( sizeof( int8_t ) * n * k ); + + int32_t* c = ( int32_t* ) malloc( sizeof( int32_t ) * m * n ); + memset( ( void* ) c, 0, sizeof( int32_t ) * m * n ); + + int32_t* c_ref = ( int32_t* ) malloc( sizeof( int32_t ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( int32_t ) * m * n ); + + int32_t alpha = 2; + int32_t beta = 9; + + fill_array( a, ( m * k ) ); + fill_array( b, ( k * n ) ); + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_u8s8s32os32(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_u8s8s32os32( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( b != NULL ) + free( b ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemm_u8s8s32os8.cpp b/gtestsuite/src/test_gemm_u8s8s32os8.cpp new file mode 100644 index 000000000..374bd42e9 --- /dev/null +++ b/gtestsuite/src/test_gemm_u8s8s32os8.cpp @@ -0,0 +1,248 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "lpgemm_utils.h" + +#ifdef BLIS_ENABLE_ADDONS +static int8_t* aocl_reorder(int8_t* b, dim_t k, dim_t n, dim_t ldb) { + siz_t b_reorder_buf_siz_req; + b_reorder_buf_siz_req = aocl_get_reorder_buf_size_u8s8s32os32('B',k, n); + int8_t* b_reorder = ( int8_t* )malloc( b_reorder_buf_siz_req ); + aocl_reorder_u8s8s32os32('B', b, b_reorder, k, n, ldb ); + return b_reorder; +} + +static void aocl_gemm_driver_u8s8s32os8( + uint32_t n_repeats, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int32_t alpha, + uint8_t* a, + dim_t lda, + char reordera, + int8_t* b, + dim_t ldb, + char reorderb, + int32_t beta, + int8_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + uint32_t i; + char storage = stor_order; + for ( i = 0; i < n_repeats; i++ ) { + memset( ( void* ) c, 0, sizeof( int8_t ) * (m * n) ); + + aocl_gemm_u8s8s32os8(storage, transa, transb, m, n, k, + alpha, a, lda, reordera, b, + ldb, reorderb, beta, c, ldc, post_op ); +/* + aocl_gemm_u8s8s32os8(storage, (const char)transa, (const char)transb, (const dim_t)m, (const dim_t)n, (const dim_t)k, + (const int8_t)alpha, (const uint8_t *)a, (const dim_t)lda, (const char)reordera, (const int8_t *)b, + (const dim_t)ldb, (const char)reorderb, (const int8_t)beta, c, (const dim_t)ldc, post_op ); +*/ + } +} + +static void mat_mul_driver_u8s8s32os8( + test_params_t* params, + char stor_order, + char transa, + char transb, + dim_t m, + dim_t n, + dim_t k, + int32_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int32_t beta, + int8_t* c, + dim_t ldc, + aocl_post_op* post_op +) { + char reordera; + char reorderb; + + if ( ( params->op_t == 'p' ) || ( params->op_t == 'P' ) ) + { + /* No reordering of B.*/ + reordera = 'n'; + reorderb = 'n'; + + aocl_gemm_driver_u8s8s32os8(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b, ldb, reorderb, beta, c, ldc, post_op ); + } + else if ( ( params->op_t == 'r' ) || ( params->op_t == 'R' ) ) + { + /* Reorder B.*/ + reordera = 'n'; + reorderb = 'r'; + + int8_t* b_reorder = aocl_reorder( b, k, n, ldb ); + + aocl_gemm_driver_u8s8s32os8(params->n_repeats, stor_order, transa, transb, m, n, k, + alpha, a, lda, reordera, b_reorder, ldb, reorderb, beta, c, ldc, post_op ); + + free( b_reorder ); + } +} + +static double mat_mul_accuracy_check_driver_u8s8s32os8 + ( + const char stor_order, + dim_t m, + dim_t n, + dim_t k, + int32_t alpha, + uint8_t* a, + dim_t lda, + int8_t* b, + dim_t ldb, + int32_t beta, + int8_t* c, + dim_t ldc, + int8_t* c_ref, + aocl_post_op* post_op, + bool dscale_out + ) +{ + double resid = 0.0; + dim_t rs_a = lda; + dim_t cs_a = 1; + dim_t rs_b = ldb; + dim_t cs_b = 1; + dim_t rs_c = ldc; + dim_t cs_c = 1; + + if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + rs_a = 1; + cs_a = lda; + rs_b = 1; + cs_b = ldb; + rs_c = 1; + cs_c = ldc; + } + + resid = mat_mul_accuracy_check_driver( m, n, k, + alpha, a, rs_a, cs_a, b, rs_b, cs_b, beta, c, rs_c, cs_c, c_ref, post_op, + dscale_out ); + + return resid; +} +#endif + +double libblis_test_op_gemm_u8s8s32os8 ( + test_params_t* params, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + double resid = 0.0; +#ifdef BLIS_ENABLE_ADDONS + dim_t m, n, k; + dim_t lda, ldb, ldc; + char* post_ops_str = NULL; + char* post_ops_str_dest = NULL; + aocl_post_op* post_op = NULL; + char stor_order = sc_str[0]; + char transa = 'n'; //pc_str[0]; + char transb = 'n'; //pc_str[1]; + bool dscale_out = true; + + stor_order = ( ( stor_order == 'r' ) || ( stor_order == 'R' ) || + ( stor_order == 'c' ) || ( stor_order == 'C' ) ) ? + stor_order : 'r'; + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->k; + n = dim->n; + + if( ( stor_order == 'r' ) || ( stor_order == 'R' ) ) + { + lda = k; // a = mxk; + ldb = n; // b = kxn; + ldc = n; // c = mxn; + } + else if ( ( stor_order == 'C' ) || ( stor_order == 'c' ) ) + { + lda = m; + ldb = k; + ldc = m; + cout << "Column Major not supported" << endl; + return resid; + } + + + /* Get 64 byte aligned memory.*/ + uint8_t* a = ( uint8_t* ) malloc( sizeof( uint8_t ) * m * k ); + + int8_t* b = ( int8_t* ) malloc( sizeof( int8_t ) * n * k ); + + int8_t* c = ( int8_t* ) malloc( sizeof( int8_t ) * m * n ); + memset( ( void* ) c, 0, sizeof( int8_t ) * m * n ); + + int8_t* c_ref = ( int8_t* ) malloc( sizeof( int8_t ) * m * n ); + memset( ( void* ) c_ref, 0, sizeof( int8_t ) * m * n ); + + int32_t alpha = 2; + int32_t beta = 9; + + fill_array( a, ( m * k ) ); + fill_array( b, ( k * n ) ); + + if ( post_ops_str != NULL ) + { + post_ops_str_dest = strdup( post_ops_str ); + } + + if( ( post_ops_str != NULL ) || ( dscale_out ) ) + { + post_op = lpgemm_create_post_ops_struct + ( m, n, post_ops_str_dest, dscale_out ); + if ( post_op == NULL ) + { + printf(" post op struct allocation failure, returning.n"); + return -1; + } + } + + mat_mul_driver_u8s8s32os8(params, stor_order, transa, transb, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, post_op ); + +#ifndef __GTEST_VALGRIND_TEST__ + resid = mat_mul_accuracy_check_driver_u8s8s32os8( stor_order, m, n, k, alpha, + a, lda, b, ldb, beta, c, ldc, c_ref, post_op, + dscale_out ); +#endif + + if ( post_op != NULL ) + lpgemm_destroy_post_ops_struct( post_op ); + + // Free the test objects. + if ( a != NULL ) + free( a ); + if ( b != NULL ) + free( b ); + if ( c != NULL ) + free( c ); + if ( c_ref != NULL ) + free( c_ref ); + + if ( post_ops_str_dest != NULL ) + free( post_ops_str_dest ); + +#else + cout << "CPU Arch do not support AVX512" << endl; +#endif + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemmt.cpp b/gtestsuite/src/test_gemmt.cpp new file mode 100644 index 000000000..f44c80fec --- /dev/null +++ b/gtestsuite/src/test_gemmt.cpp @@ -0,0 +1,597 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_gemmt.h" + +void libblis_test_gemmt_impl( + iface_t iface, + obj_t *alpha, + obj_t *a, + obj_t *b, + obj_t *beta, + obj_t *c +); + +double libblis_test_gemmt_check( + test_params_t *params, + obj_t *alpha, + obj_t *a, + obj_t *b, + obj_t *beta, + obj_t *c, + obj_t *c_orig +); + +double cblas_gemmt( + test_params_t* params, + uplo_t uploc, + f77_int n, + f77_int k, + f77_int lda, + f77_int ldb, + f77_int ldc, + obj_t* a, + obj_t* b, + obj_t* c, + obj_t* alpha, + obj_t* beta, + num_t dt, + trans_t transa, + trans_t transb +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_TRANSPOSE cblas_transa; + enum CBLAS_TRANSPOSE cblas_transb; + enum CBLAS_UPLO cblas_uplo; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploc ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if( bli_is_trans( transa ) ) + cblas_transa = CblasTrans; + else if( bli_is_conjtrans( transa ) ) + cblas_transa = CblasConjTrans; + else + cblas_transa = CblasNoTrans; + + if( bli_is_trans( transb ) ) + cblas_transb = CblasTrans; + else if( bli_is_conjtrans( transb ) ) + cblas_transb = CblasConjTrans; + else + cblas_transb = CblasNoTrans; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_sgemmt( cblas_order, cblas_uplo, cblas_transa, cblas_transb, + n, k, *alphap, ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dgemmt( cblas_order, cblas_uplo, cblas_transa, cblas_transb, + n, k, *alphap, ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_cgemmt( cblas_order, cblas_uplo, cblas_transa, cblas_transb, + n, k, alphap, ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zgemmt( cblas_order, cblas_uplo, cblas_transa, cblas_transb, + n, k, alphap, ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return 0; +} + +double blas_gemmt( + f77_char f77_uploc, + f77_int n, + f77_int k, + f77_int lda, + f77_int ldb, + f77_int ldc, + obj_t* a, + obj_t* b, + obj_t* c, + obj_t* alpha, + obj_t* beta, + num_t dt, + f77_char f77_transa, + f77_char f77_transb +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + sgemmt_( &f77_uploc, &f77_transa, &f77_transb, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dgemmt_( &f77_uploc, &f77_transa, &f77_transb, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cgemmt_( &f77_uploc, &f77_transa, &f77_transb, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zgemmt_( &f77_uploc, &f77_transa, &f77_transb, &n, &k, alphap, ap, + (f77_int*)&lda, bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_gemmt( + test_params_t* params, + iface_t iface, + obj_t *alpha, + obj_t *a, + obj_t *b, + obj_t *beta, + obj_t *c, + num_t dt +) { + + if(params->api == API_BLIS) { + libblis_test_gemmt_impl(iface, alpha, a, b, beta, c); + } + else { /*CLBAS || BLAS */ + f77_int kk = bli_obj_width_after_trans( a ); + f77_int nn = bli_obj_width( c ); + uplo_t uploc = bli_obj_uplo( c ); + trans_t transa = bli_obj_conjtrans_status( a ); + trans_t transb = bli_obj_conjtrans_status( b ); + f77_int lda, ldb, ldc; + + if( bli_obj_row_stride( c ) == 1 ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + ldc = ldc + params->ld[2]; + } + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(bli_obj_has_notrans(b) && bli_obj_has_conj(b)) { + conjugate_tensor(b, dt); + transb = bli_obj_onlytrans_status( b ); + } + + if(params->api == API_CBLAS) { + cblas_gemmt( params, uploc, nn, kk, lda, ldb, ldc, a, b, c, + alpha, beta, dt, transa, transb); + } else { /**/ + f77_char f77_transa; + f77_char f77_transb; + f77_char f77_uploc; + + if(transa == BLIS_TRANSPOSE) f77_transa='T'; + else if ( transa == BLIS_CONJ_TRANSPOSE ) f77_transa='C'; + else /*if ( transa == BLIS_NO_TRANSPOSE )*/ f77_transa='N'; + + if(transb == BLIS_TRANSPOSE) f77_transb='T'; + else if ( transb == BLIS_CONJ_TRANSPOSE ) f77_transb='C'; + else /*if ( transb == BLIS_NO_TRANSPOSE )*/ f77_transb='N'; + + if( bli_obj_row_stride( c ) == 1 ) { + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + blas_gemmt(f77_uploc, nn, kk, lda, ldb, ldc, a, b, c, + alpha, beta, dt, f77_transa, f77_transb); + }else { + if( uploc == BLIS_UPPER) + uploc = BLIS_LOWER; + else if(uploc == BLIS_LOWER) + uploc = BLIS_UPPER; + + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + blas_gemmt(f77_uploc, nn, kk, ldb, lda, ldc, b, a, c, + alpha, beta, dt, f77_transb, f77_transa); + } + } + } + return ; +} + +double libblis_ref_gemmt( + test_params_t* params, + obj_t * alpha, + obj_t * a, + obj_t * b, + obj_t * beta, + obj_t * c, + obj_t * c_ref, + obj_t * c_orig +){ + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_gemmt( c ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_gemmt_check(params, alpha, a, b, beta, c, c_ref); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_igemmt_check(params, alpha, a, b, beta, c, c_orig); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_gemmt( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_gemmt_impl(iface, alpha, a, b, beta, r); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_gemmt ( + test_params_t *params, + iface_t iface, + char *dc_str, + char *pc_str, + char *sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m, k; + uplo_t uploc; + trans_t transa, transb; + obj_t alpha, a, b, beta; + obj_t c, c_ref, c_org_tri, c_result_tri, c_save; + double resid = 0.0; + obj_t aa,bb; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt(dc_str[0], &datatype); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo(pc_str[0], &uploc); + bli_param_map_char_to_blis_trans(pc_str[1], &transa); + bli_param_map_char_to_blis_trans(pc_str[2], &transb); + + // Create test scalars. + bli_obj_scalar_init_detached(datatype, &alpha); + bli_obj_scalar_init_detached(datatype, &beta); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create(params, datatype, transa, + sc_str[1], m, k, &a); + libblis_test_mobj_create(params, datatype, transa, + sc_str[1], m, k, &aa); + libblis_test_mobj_create(params, datatype, transb, + sc_str[2], k, m, &b); + libblis_test_mobj_create(params, datatype, transb, + sc_str[2], k, m, &bb); + libblis_test_mobj_create(params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c); + libblis_test_mobj_create(params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_save); + libblis_test_mobj_create(params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_ref); + libblis_test_mobj_create(params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_org_tri); + libblis_test_mobj_create(params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_result_tri); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if (bli_obj_is_real(&c)) { + bli_setsc(alpv.real, 0.0, &alpha); + bli_setsc(betv.real, 0.0, &beta); + } + else { + // For gemmt, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + bli_setsc(alpv.real, (alpv.real/0.8), &alpha); + bli_setsc(betv.real, (betv.real/1.2), &beta); + } + // Randomize A and B + libblis_test_mobj_randomize(params, TRUE, &a); + libblis_test_mobj_randomize(params, TRUE, &b); + + // Generate random input matrix + libblis_test_mobj_randomize(params, TRUE, &c); + } + else { + int32_t x = (int32_t)alpv.real; + int32_t y = (int32_t)betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + + // Randomize A and B + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + + // Generate random input matrix + libblis_test_mobj_irandomize( params, &c ); + } + + bli_copym( &a, &aa ); + bli_copym( &b, &bb ); + + // Apply the remaining parameters. + // We need to do this before we create the referece matrix + bli_obj_set_conjtrans(transa, &a); + bli_obj_set_conjtrans(transb, &b); + + // Create the requried copies before setting the uplo attribute + bli_copym(&c, &c_save); + bli_copym(&c, &c_org_tri); + bli_copym(&c, &c_result_tri); + bli_obj_set_uplo(uploc, &c); + bli_obj_set_uplo(uploc, &c_save); + + // Create c_org_tri matrix using setm operation, this matrix will + // have original values from input matrix "c" for all elements outside + // triangle selected for GEMMT operation. + bli_obj_set_uplo(uploc, &c_org_tri); // Set to request uplo to set all elemnts in triangle to zero + bli_setm(&BLIS_ZERO, &c_org_tri); + bli_obj_toggle_uplo(&c_org_tri); // Toggle uplo now so that untouched triangle is active. + + // GEMMT output is same as GEMM for the triangle selected by uplo + // So we want to extract this triangle from complete GEMM results + // We do this by setting the uplo and converting the results + // to triangluer matrix. + // Perform gemm operation on original inputs + bli_gemm(&alpha, &a, &b, &beta, &c_result_tri); + // Set the values in other triangle to zero by converting it to trianguler matrix + bli_obj_set_uplo(uploc, &c_result_tri); + bli_mktrim(&c_result_tri); + + // Now we have two matrices with opposite triangles set to zero + // c_result_tri: It has output of GEMM in selected triangle (including diagonal) + // Rest of its elements are set to zero. + // c_org_tri: It has values from orignal C matrix in the non-selected triangle + // Rest of the elements including diagonal are set to zero + // The result of the GEMMT operation will be combined matrix of thse two matrics + // So add them togher + bli_setm(&BLIS_ZERO, &c_ref); // Both matrices we are going to add, have uplo settings + // Clear the destination matrix to avoid partial updates + bli_copym(&c_org_tri, &c_ref); + bli_addm(&c_result_tri, &c_ref); + + if ((params->nanf)) { + test_fillbuffmem(&c, datatype ); + } + + //Copy c to c_save + bli_copym( &c, &c_save ); + + bli_obj_set_conjtrans( transa, &aa ); + bli_obj_set_conjtrans( transb, &bb ); + + libblis_api_gemmt(params, iface, &alpha, &aa, &bb, &beta, &c, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create(params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &r); + + resid = libblis_test_bitrp_gemmt( params, iface, &alpha, &a, &b, &beta, + &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_gemmt(params, &alpha, &a, &b, &beta, &c, &c_ref, &c_save); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem(&c, &resid); + + // Free the test objects. + libblis_test_obj_free(&a); + libblis_test_obj_free(&aa); + libblis_test_obj_free(&b); + libblis_test_obj_free(&bb); + libblis_test_obj_free(&c); + libblis_test_obj_free(&c_ref); + libblis_test_obj_free(&c_org_tri); + libblis_test_obj_free(&c_result_tri); + libblis_test_obj_free(&c_save); + + return abs(resid); +} + +void libblis_test_gemmt_impl( + iface_t iface, + obj_t *alpha, + obj_t *a, + obj_t *b, + obj_t *beta, + obj_t *c +) { + switch (iface) { + case BLIS_TEST_SEQ_FRONT_END: + bli_gemmt(alpha, a, b, beta, c); + break; + + default: + libblis_test_printf_error("Invalid interface type.\n"); + } +} + +double libblis_test_gemmt_check( + test_params_t *params, + obj_t *alpha, + obj_t *a, + obj_t *b, + obj_t *beta, + obj_t *c, + obj_t *c_orig +) { + num_t dt = bli_obj_dt(c); + num_t dt_real = bli_obj_dt_proj_to_real(c); + + dim_t m = bli_obj_length(c); + + obj_t norm; + obj_t t, v, z; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized. + // - b is randomized. + // - c is randomized with uplo set + // + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transa(B) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // z = C * C_reference + // + // + + bli_obj_scalar_init_detached(dt_real, &norm); + + bli_obj_create(dt, m, 1, 0, 0, &t); + bli_obj_create(dt, m, 1, 0, 0, &v); + bli_obj_create(dt, m, 1, 0, 0, &z); + + libblis_test_vobj_randomize(params, TRUE, &t); + + // Ensure result metrix has only selected triangle. + // Calculate V = C * t + bli_gemv(&BLIS_ONE, c, &t, &BLIS_ZERO, &v); + bli_gemv(&BLIS_ONE, c_orig, &t, &BLIS_ZERO, &z); + + // Find the norm + bli_subv(&z, &v); + bli_normfv(&v, &norm); + bli_getsc(&norm, &resid, &junk); + + bli_obj_free(&t); + bli_obj_free(&v); + bli_obj_free(&z); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemmt.h b/gtestsuite/src/test_gemmt.h new file mode 100644 index 000000000..2e8e88fb8 --- /dev/null +++ b/gtestsuite/src/test_gemmt.h @@ -0,0 +1,95 @@ +#ifndef TEST_GEMMT_H +#define TEST_GEMMT_H + +#include "blis_test.h" + +double libblis_test_igemmt_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +template +void libblis_igemv_check + ( + trans_t transA, + dim_t M, + dim_t N, + T* alpha, + T* A, + dim_t rsa, + dim_t csa, + T* X, + dim_t incx, + T* beta, + T* Y, + dim_t incy + ); + +template +void libblis_icgemv_check + ( + trans_t transA, + dim_t M, + dim_t N, + T* alpha, + T* A, + dim_t rsa, + dim_t csa, + bool conja, + T* X, + dim_t incx, + T* beta, + T* Y, + dim_t incy, + bool conjx + ); + +template +void libblis_igemm_check + ( + dim_t M, + dim_t N, + dim_t K, + T* alpha, + T* A, + dim_t rsa, + dim_t csa, + T* B, + dim_t rsb, + dim_t csb, + T* beta, + T* C, + dim_t rsc, + dim_t csc + ); + +template +void libblis_icgemm_check + ( + dim_t M, + dim_t N, + dim_t K, + T* alpha, + T* A, + dim_t rsa, + dim_t csa, + bool conja, + T* B, + dim_t rsb, + dim_t csb, + bool conjb, + T* beta, + T* C, + dim_t rsc, + dim_t csc + ); + +double libblis_check_nan_gemmt( obj_t* c ); + +#endif /* TEST_GEMMT_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_gemv.cpp b/gtestsuite/src/test_gemv.cpp new file mode 100644 index 000000000..773468e76 --- /dev/null +++ b/gtestsuite/src/test_gemv.cpp @@ -0,0 +1,536 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_gemv.h" + +// Local prototypes. +void libblis_test_gemv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_gemv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +); + +double libblis_test_gemv_check ( + test_params_t* params, + obj_t* kappa, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +void cblas_gemv( + f77_int m, + f77_int n, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + trans_t transa, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_TRANSPOSE cblas_trans; + + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_trans(transa ) ) + cblas_trans = CblasTrans; + else if( bli_is_conjtrans(transa ) ) + cblas_trans = CblasConjTrans; + else + cblas_trans = CblasNoTrans; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* betap = (float*) bli_obj_buffer( beta ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_sgemv( cblas_order, cblas_trans, m, n, + *alphap, ap, lda, xp, incx, *betap, yp, incy ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* betap = (double*) bli_obj_buffer( beta ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dgemv( cblas_order, cblas_trans, m, n, + *alphap, ap, lda, xp, incx, *betap, yp, incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_cgemv( cblas_order, cblas_trans, m, n, + alphap, ap, lda, xp, incx, betap, yp, incy ); + + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zgemv( cblas_order, cblas_trans, m, n, + alphap, ap, lda, xp, incx, betap, yp, incy ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void gemv_(f77_char f77_trans, f77_int m, f77_int n, scomplex* alpha, +scomplex* ap, f77_int lda, scomplex* xp, f77_int incx, scomplex* beta, +scomplex* yp, f77_int incy){ + cgemv_( &f77_trans, &m, &n, alpha, ap, &lda, xp, &incx, beta, yp, &incy); +} + +void gemv_(f77_char f77_trans, f77_int m, f77_int n, dcomplex* alpha, +dcomplex* ap, f77_int lda, dcomplex* xp, f77_int incx, dcomplex* beta, +dcomplex* yp, f77_int incy){ + zgemv_( &f77_trans, &m, &n, alpha, ap, &lda, xp, &incx, beta, yp, &incy); +} + +void blas_gemv( + trans_t transa, + f77_int m, + f77_int n, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + f77_char f77_trans; + + bli_param_map_blis_to_netlib_trans( transa, &f77_trans ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* betap = (float*) bli_obj_buffer( beta ); + float* yp = (float*) bli_obj_buffer( y ); + sgemv_( &f77_trans, &m, &n, alphap, ap, &lda, xp, + &incx, betap, yp, &incy ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* betap = (double*) bli_obj_buffer( beta ); + double* yp = (double*) bli_obj_buffer( y ); + dgemv_( &f77_trans, &m, &n, alphap, ap, &lda, xp, + &incx, betap, yp, &incy ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cgemv_( &f77_trans, &m, &n, alphap, ap, &lda, xp, + &incx, betap, yp, &incy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zgemv_( &f77_trans, &m, &n, alphap, ap, &lda, xp, + &incx, betap, yp, &incy ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_gemv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + num_t dt +) { + + if(params->api == API_BLIS) { + libblis_test_gemv_impl( iface, alpha, a, x, beta, y ); + } + else { /*CLBAS || BLAS */ + f77_int mm = bli_obj_length( a ); + f77_int nn = bli_obj_width( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + trans_t transa = bli_obj_conjtrans_status( a ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(bli_obj_has_conj(x)) { + conjugate_tensor(x, dt); + } + + if(params->api == API_CBLAS) { + cblas_gemv(mm, nn, alpha, a, lda, x, incx, beta, y, incy, transa, dt); + } + else { /**/ + if( bli_obj_row_stride( a ) == 1 ) { + blas_gemv(transa, mm, nn, alpha, a, lda, x, incx, beta, y, incy, dt); + } + else { + blas_gemv(transa, nn, mm, alpha, a, lda, x, incx, beta, y, incy, dt); + } + } + + if(bli_obj_has_conj(x)) { + conjugate_tensor(x, dt); + } + } + return ; +} + +double libblis_ref_gemv( + test_params_t* params, + obj_t* kappa, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + num_t dt +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_gemv_check( params, kappa, alpha, + a, x, beta, y, y_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_igemv_check( alpha, + a, x, beta, y, y_orig, dt); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_gemv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_gemv_impl( iface, alpha, a, x, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + + +double libblis_test_op_gemv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m, n; + trans_t transa; + conj_t conjx; + obj_t kappa; + obj_t alpha, a, x, beta, y; + obj_t y_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + if(params->api != API_BLIS) { + bli_param_map_char_to_blas_trans( pc_str[0], &transa ); + } else { + bli_param_map_char_to_blis_trans( pc_str[0], &transa ); + } + + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &kappa ); + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transa, + sc_str[0], m, n, &a ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[0], m, n, &aa ); + libblis_test_vobj_create( params, datatype, + sc_str[1], n, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[2], m, &y ); + libblis_test_vobj_create( params, datatype, + sc_str[2], m, &y_save ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &y ) ) { + bli_setsc(alpv.real, 0.0, &alpha); + bli_setsc(betv.real, 0.0, &beta); + } + else { + bli_setsc(alpv.real, (alpv.real/0.8), &alpha); + bli_setsc(betv.real, (betv.real/1.2), &beta); + } + + // Randomize x and y, and save y. + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + } + else{ + int32_t xx = (int32_t)alpv.real; + int32_t yy = (int32_t)betv.real; + if ( bli_obj_is_real( &y ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + bli_setsc( (double)yy, 0.0, &beta ); + } + else { + // For syrk, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + int32_t ac = (int32_t)(xx/0.8); + int32_t bc = (int32_t)(yy/1.0); + bli_setsc( (double)xx, (double)ac, &alpha ); + bli_setsc( (double)yy, (double)bc, &beta ); + } + + // Randomize x and y, and save y. + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + // Initialize diagonal of matrix A. + bli_setsc( 2.0, -1.0, &kappa ); + bli_setm( &BLIS_ZERO, &a ); + bli_setd( &kappa, &a ); + + bli_copym( &a, &aa ); + + // Apply the parameters. + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &y, &y_save ); + + bli_obj_set_conjtrans( transa, &aa ); + + libblis_api_gemv(params, iface, &alpha, &aa, &x, &beta, &y, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], m, &r ); + + resid = libblis_test_bitrp_gemv(params, iface, &alpha, &a, &x, &beta, + &y, &y_save, &r, datatype ); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_gemv(params, &kappa, &alpha, &a, &x, &beta, + &y, &y_save, datatype ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_gemv_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +) { + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_gemv( alpha, a, x, beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_gemv_check ( + test_params_t* params, + obj_t* kappa, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + conj_t conja = bli_obj_conj_status( a ); + + dim_t n_x = bli_obj_vector_dim( x ); + dim_t m_y = bli_obj_vector_dim( y ); + + dim_t min_m_n = bli_min( m_y, n_x ); + + obj_t x_temp, y_temp; + obj_t kappac, norm; + obj_t xT_temp, yT_temp, yT; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is initialized to kappa along the diagonal. + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha, beta, and kappa should have non-zero imaginary components in + // the complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + alpha * transa(A) * conjx(x) + // + // is functioning correctly if + // + // normfv( y - z ) + // + // is negligible, where + // + // z = beta * y_orig + alpha * conja(kappa) * x + // + + bli_obj_scalar_init_detached_copy_of( dt, conja, kappa, &kappac ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, n_x, 1, 0, 0, &x_temp ); + bli_obj_create( dt, m_y, 1, 0, 0, &y_temp ); + + bli_copyv( x, &x_temp ); + bli_copyv( y_orig, &y_temp ); + + bli_acquire_vpart_f2b( BLIS_SUBPART1, 0, min_m_n, + &x_temp, &xT_temp ); + bli_acquire_vpart_f2b( BLIS_SUBPART1, 0, min_m_n, + &y_temp, &yT_temp ); + bli_acquire_vpart_f2b( BLIS_SUBPART1, 0, min_m_n, + y, &yT ); + + bli_scalv( &kappac, &xT_temp ); + bli_scalv( beta, &yT_temp ); + bli_axpyv( alpha, &xT_temp, &yT_temp ); + + bli_subv( &yT_temp, &yT ); + bli_normfv( &yT, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_gemv.h b/gtestsuite/src/test_gemv.h new file mode 100644 index 000000000..1b3ef82c6 --- /dev/null +++ b/gtestsuite/src/test_gemv.h @@ -0,0 +1,19 @@ +#ifndef TEST_GEMV_H +#define TEST_GEMV_H + +#include "blis_test.h" + +double libblis_test_igemv_check + ( + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + num_t dt + ); + +double libblis_check_nan_gemv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_GEMV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_ger.cpp b/gtestsuite/src/test_ger.cpp new file mode 100644 index 000000000..a6dbdbac7 --- /dev/null +++ b/gtestsuite/src/test_ger.cpp @@ -0,0 +1,455 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_ger.h" + +// Local prototypes. +void libblis_test_ger_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_ger_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a +); + +double libblis_test_ger_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +); + +void cblas_ger( + f77_int m, + f77_int n, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* a, + f77_int lda, + num_t dt +){ + + enum CBLAS_ORDER cblas_order; + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_sger(cblas_order, m, n, *alphap, xp, incx, yp, incy, ap, lda ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dger(cblas_order, m, n, *alphap, xp, incx, yp, incy, ap, lda ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + if( bli_obj_has_conj(x) != bli_obj_has_conj(y)) { + cblas_cgerc(cblas_order, m, n, alphap, xp, incx, yp, incy, ap, lda ); + } + else{ + cblas_cgeru(cblas_order, m, n, alphap, xp, incx, yp, incy, ap, lda ); + } + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + if( bli_obj_has_conj(x) != bli_obj_has_conj(y)) { + cblas_zgerc(cblas_order, m, n, alphap, xp, incx, yp, incy, ap, lda ); + } + else{ + cblas_zgeru(cblas_order, m, n, alphap, xp, incx, yp, incy, ap, lda ); + } + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_ger( + f77_int m, + f77_int n, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* a, + f77_int lda, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + sger_(&m, &n, alphap, xp, &incx, yp, &incy, ap, &lda ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + dger_(&m, &n, alphap, xp, &incx, yp, &incy, ap, &lda ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + if( bli_obj_has_conj(x) != bli_obj_has_conj(y)) { + cgerc_(&m, &n, alphap, xp, &incx, yp, &incy, ap, &lda ); + } + else { + cgeru_(&m, &n, alphap, xp, &incx, yp, &incy, ap, &lda ); + } + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + if( bli_obj_has_conj(x) != bli_obj_has_conj(y)) { + zgerc_(&m, &n, alphap, xp, &incx, yp, &incy, ap, &lda ); + } + else{ + zgeru_(&m, &n, alphap, xp, &incx, yp, &incy, ap, &lda ); + } + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_ger( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_ger_impl( iface, alpha, x, y, a ); + } + else { /*CLBAS || BLAS */ + f77_int mm = bli_obj_length( a ); + f77_int nn = bli_obj_width( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if(params->api == API_CBLAS) { + cblas_ger(mm, nn, alpha, x, incx, y, incy, a, lda, dt ); + } + else { /**/ + if ( bli_obj_row_stride( a ) == 1 ){ + blas_ger(mm, nn, alpha, x, incx, y, incy, a, lda, dt ); + } + else { + blas_ger(nn, mm, alpha, y, incy, x, incx, a, lda, dt ); + } + } + } + return ; +} + +double libblis_ref_ger( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_ger_check( params, alpha, x, y, a, a_save); + } + else { + resid = libblis_test_iger_check( params, alpha, x, y, a, a_save); + } + return resid; +} + +double libblis_test_bitrp_ger( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( a_orig, r ); + libblis_test_ger_impl( iface, alpha, x, y, r ); + resid = libblis_test_bitrp_vector(a, r, dt); + } + return resid; +} + +double libblis_test_op_ger ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m, n; + conj_t conjx, conjy; + obj_t alpha, x, y, a; + obj_t a_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[1], n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, n, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, n, &a_save ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &y ) ) { + //bli_setsc(alpv, 0.0, &alpha); + bli_setsc( -1.0, 1.0, &alpha ); + } + else { + //bli_setsc(alpv, (alpv/0.8), &alpha); + bli_setsc( -1.0, 1.0, &alpha ); + } + + // Randomize x and y. + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + } + else{ + int32_t xx = (int32_t)alpv.real; + if ( bli_obj_is_real( &y ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + } + else { + // For syrk, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + int32_t ac = (int32_t)(xx/0.8); + bli_setsc( (double)xx, (double)ac, &alpha ); + } + + // Randomize x and y, and save y. + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + // Initialize A to identity and save. + bli_setm( &BLIS_ZERO, &a ); + bli_setd( &BLIS_ONE, &a ); + bli_copym( &a, &a_save ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + // Perform checks. + libblis_api_ger(params, iface, &alpha, &x, &y, &a, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, n, &r ); + + resid = libblis_test_bitrp_ger( params, iface, &alpha, &x, &y, &a, + &a_save, &r, datatype ); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_ger( params, &alpha, &x, &y, &a, &a_save); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &a, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &a ); + libblis_test_obj_free( &a_save ); + + return abs(resid); +} + +void libblis_test_ger_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a +){ + switch( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_ger( alpha, x, y, a ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_ger_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + num_t dt = bli_obj_dt( a ); + num_t dt_real = bli_obj_dt_proj_to_real( a ); + + dim_t m_a = bli_obj_length( a ); + dim_t n_a = bli_obj_width( a ); + + obj_t t, v, w; + obj_t rho, norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y is randomized. + // - a is identity. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // A := A_orig + alpha * conjx(x) * conjy(y) + // + // is functioning correctly if + // + // normfv( v - w ) + // + // is negligible, where + // + // v = A * t + // w = ( A_orig + alpha * conjx(x) * conjy(y)^T ) * t + // = A_orig * t + alpha * conjx(x) * conjy(y)^T * t + // = A_orig * t + alpha * conjx(x) * rho + // = A_orig * t + w + // + + bli_obj_scalar_init_detached( dt, &rho ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, n_a, 1, 0, 0, &t ); + bli_obj_create( dt, m_a, 1, 0, 0, &v ); + bli_obj_create( dt, m_a, 1, 0, 0, &w ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, a, &t, &BLIS_ZERO, &v ); + + bli_dotv( y, &t, &rho ); + bli_mulsc( alpha, &rho ); + bli_scal2v( &rho, x, &w ); + bli_gemv( &BLIS_ONE, a_orig, &t, &BLIS_ONE, &w ); + + bli_subv( &w, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_ger.h b/gtestsuite/src/test_ger.h new file mode 100644 index 000000000..784374ed0 --- /dev/null +++ b/gtestsuite/src/test_ger.h @@ -0,0 +1,18 @@ +#ifndef TEST_GER_H +#define TEST_GER_H + +#include "blis_test.h" + +double libblis_test_iger_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig + ); + +double libblis_check_nan_ger( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_GER_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_hemm.cpp b/gtestsuite/src/test_hemm.cpp new file mode 100644 index 000000000..37a87be49 --- /dev/null +++ b/gtestsuite/src/test_hemm.cpp @@ -0,0 +1,551 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_hemm.h" + +using namespace std; + +// Local prototypes. +void libblis_test_hemm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_hemm_impl( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +); + +double libblis_test_hemm_check( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_hemm( + side_t side, + uplo_t uploa, + f77_int mm, + f77_int nn, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_SIDE cblas_side; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if(bli_is_upper(uploa)) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if(bli_is_left(side)) + cblas_side = CblasLeft; + else + cblas_side = CblasRight; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_ssymm( cblas_order, cblas_side, cblas_uplo, mm, nn, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dsymm( cblas_order, cblas_side, cblas_uplo, mm, nn, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_chemm( cblas_order, cblas_side, cblas_uplo, mm, nn, alphap, + ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zhemm( cblas_order, cblas_side, cblas_uplo, mm, nn, alphap, + ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return 0; +} + +double blas_hemm( + side_t side, + uplo_t uploa, + f77_int mm, + f77_int nn, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + + f77_char f77_side; + f77_char f77_uploa; + + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + ssymm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dsymm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + chemm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zhemm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_hemm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_hemm_impl( iface, side, alpha, a, b, beta, c ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( c ); + f77_int nn = bli_obj_width( c ); + f77_int lda, ldb, ldc; + + if( bli_obj_row_stride( c ) == 1 ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + ldc = ldc + params->ld[2]; + } + + if(params->api == API_CBLAS) { + cblas_hemm( side, uploa, mm, nn, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } else { /**/ + if( bli_obj_row_stride( c ) == 1 ) { + blas_hemm( side, uploa, mm, nn, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + else { + if( side == BLIS_LEFT) + side = BLIS_RIGHT; + else if(side == BLIS_RIGHT) + side = BLIS_LEFT; + + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + blas_hemm( side, uploa, nn, mm, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + } + } + return ; +} + +double libblis_ref_hemm( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_save, + num_t dt +) { + + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_hemm(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_hemm_check( params, side, alpha, a, b, beta, c, c_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_ihemm_check( params, side, alpha, a, b, beta, c, c_save ); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_hemm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_hemm_impl( iface, side, alpha, a, b, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_hemm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, n; + dim_t mn_side; + side_t side; + uplo_t uploa; + obj_t alpha, a, b, beta, c; + obj_t c_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_side( pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( pc_str[1], &uploa ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + bli_set_dim_with_side( side, m, n, &mn_side ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], mn_side, mn_side, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, n, &b ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_HERMITIAN, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, (betv.real/1.2), &beta ); + } + // Randomize A, B, and C, and save C. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_mobj_randomize( params, TRUE, &b ); + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)1.0; //alpv.real; + int32_t y = (int32_t)1.0; //betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + libblis_test_mobj_irandomize( params, &c ); + } + + if ((params->nanf) && (betv.real == 0) ) { + test_fillbuffmem(&c, datatype ); + } + + // Randomize A, make it densely Hermitian, and zero the unstored triangle + // to ensure the implementation reads only from the stored region. + bli_mkherm( &a ); + bli_mktrim( &a ); + + //Copy c to c_save + bli_copym( &c, &c_save ); + + libblis_api_hemm(params, iface, side, &alpha, &a, &b, &beta, &c, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + resid = libblis_test_bitrp_hemm( params, iface, side,&alpha, &a, &b, + &beta, &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_hemm(params, side, &alpha, &a, &b, &beta, + &c, &c_save, datatype ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return resid; +} + +void libblis_test_hemm_impl ( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +){ + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_hemm( side, alpha, a, b, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_hemm_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +) { + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t n = bli_obj_width( c ); + + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized and Hermitian. + // - b is randomized. + // - c_orig is randomized. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * conja(A) * transb(B) (side = left) + // C := beta * C_orig + alpha * transb(B) * conja(A) (side = right) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // + // z = ( beta * C_orig + alpha * conja(A) * transb(B) ) * t (side = left) + // = beta * C_orig * t + alpha * conja(A) * transb(B) * t + // = beta * C_orig * t + alpha * conja(A) * w + // = beta * C_orig * t + z + // + // z = ( beta * C_orig + alpha * transb(B) * conja(A) ) * t (side = right) + // = beta * C_orig * t + alpha * transb(B) * conja(A) * t + // = beta * C_orig * t + alpha * transb(B) * w + // = beta * C_orig * t + z + + bli_obj_scalar_init_detached( dt_real, &norm ); + + if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, m, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + else // else if ( bli_is_right( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, n, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + if ( bli_is_left( side ) ) + { + bli_gemv( &BLIS_ONE, b, &t, &BLIS_ZERO, &w ); + bli_hemv( alpha, a, &w, &BLIS_ZERO, &z ); + } + else // else if ( bli_is_right( side ) ) + { + bli_hemv( &BLIS_ONE, a, &t, &BLIS_ZERO, &w ); + bli_gemv( alpha, b, &w, &BLIS_ZERO, &z ); + } + + bli_gemv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_hemm.h b/gtestsuite/src/test_hemm.h new file mode 100644 index 000000000..68713eec3 --- /dev/null +++ b/gtestsuite/src/test_hemm.h @@ -0,0 +1,20 @@ +#ifndef TEST_HEMM_H +#define TEST_HEMM_H + +#include "blis_test.h" + +double libblis_test_ihemm_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_hemm(obj_t* c, num_t dt ); + +#endif /* TEST_HEMM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_hemv.cpp b/gtestsuite/src/test_hemv.cpp new file mode 100644 index 000000000..9a6c2bf71 --- /dev/null +++ b/gtestsuite/src/test_hemv.cpp @@ -0,0 +1,497 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_hemv.h" + +// Local prototypes. +void libblis_test_hemv_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_hemv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +); + +double libblis_test_hemv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +void cblas_hemv( + uplo_t uploa, + f77_int m, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uplo ; + + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploa ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* betap = (float*) bli_obj_buffer( beta ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_ssymv(cblas_order, cblas_uplo, m, *alphap, ap, lda, xp, incx, + *betap, yp, incy); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* betap = (double*) bli_obj_buffer( beta ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dsymv(cblas_order, cblas_uplo, m, *alphap, ap, lda, xp, incx, + *betap, yp, incy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_chemv(cblas_order, cblas_uplo, m, alphap, ap, lda, xp, incx, + betap, yp, incy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zhemv(cblas_order, cblas_uplo, m, alphap, ap, lda, xp, incx, + betap, yp, incy); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_hemv( + f77_char f77_uploa, + f77_int m, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* betap = (float*) bli_obj_buffer( beta ); + float* yp = (float*) bli_obj_buffer( y ); + ssymv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* betap = (double*) bli_obj_buffer( beta ); + double* yp = (double*) bli_obj_buffer( y ); + dsymv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + chemv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zhemv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_hemv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +){ + + if(params->api == API_BLIS) { + libblis_test_hemv_impl(iface, alpha, a, x, beta, y); + } + else { /*CLBAS || BLAS */ + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if( bli_obj_has_conj(a) ) { + conjugate_tensor(a, dt); + } + if( bli_obj_has_conj(x) ) { + conjugate_tensor(x, dt); + } + + if(params->api == API_CBLAS) { + cblas_hemv(uploa, mm, alpha, a, lda, x, incx, beta, y, incy, dt ); + } + else { /**/ + f77_char f77_uploa; + if ( bli_obj_row_stride( a ) == 1 ){ + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_hemv(f77_uploa, mm, alpha, a, lda, x, incx, beta, y, incy, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_hemv(f77_uploa, mm, alpha, a, lda, x, incx, beta, y, incy, dt ); + } + } + } + return ; +} + +double libblis_ref_hemv( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_hemv_check( params, alpha, a, x, beta, y, y_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_ihemv_check( params, alpha, a, x, beta, y, y_orig ); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_hemv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_hemv_impl( iface, alpha, a, x, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_hemv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + conj_t conja; + conj_t conjx; + obj_t alpha, a, x, beta, y; + obj_t y_save; + double resid = 0.0; + obj_t aa, xx; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_conj( pc_str[1], &conja ); + bli_param_map_char_to_blis_conj( pc_str[2], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &aa ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &xx ); + libblis_test_vobj_create( params, datatype, + sc_str[2], m, &y ); + libblis_test_vobj_create( params, datatype, + sc_str[2], m, &y_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_HERMITIAN, &a ); + bli_obj_set_uplo( uploa, &a ); + + bli_obj_set_struc( BLIS_HERMITIAN, &aa ); + bli_obj_set_uplo( uploa, &aa ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &y ) ){ + bli_setsc( 1.0, 0.0, &alpha ); + bli_setsc( -1.0, 0.0, &beta ); + } + else{ + bli_setsc( 0.5, 0.5, &alpha ); + bli_setsc( -0.5, 0.5, &beta ); + } + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + } + else { + int32_t xx = (int32_t) 1.0; + int32_t yy = (int32_t)-1.0; + if ( bli_obj_is_real( &y ) ){ + bli_setsc( xx, 0.0, &alpha ); + bli_setsc( yy, 0.0, &beta ); + } + else{ + xx = (int32_t)(xx/0.8); + yy = (int32_t)(yy/1.5); + bli_setsc( xx, (xx+yy), &alpha ); + bli_setsc( yy, (xx-yy), &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + // Randomize A, make it densely Hermitian, and zero the unstored triangle + // to ensure the implementation reads only from the stored region. + bli_mkherm( &a ); + bli_mktrim( &a ); + + // Randomize x and y, and save y. + bli_copyv( &y, &y_save ); + + bli_copym( &a, &aa ); + bli_copyv( &x, &xx ); + + // Apply the remaining parameters. + bli_obj_set_conj( conja, &a ); + bli_obj_set_conj( conjx, &x ); + + bli_obj_set_conj( conja, &aa ); + bli_obj_set_conj( conjx, &xx ); + + libblis_api_hemv(params, iface, &alpha, &aa, &xx, &beta, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], m, &r ); + + resid = libblis_test_bitrp_hemv( params, iface, &alpha, &a, &x, + &beta, &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_hemv( params, &alpha, &a, &x, &beta, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_hemv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_hemv( alpha, a, x, beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_hemv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t v; + obj_t norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized and Hermitian. + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + alpha * conja(A) * conjx(x) + // + // is functioning correctly if + // + // normfv( y - v ) + // + // is negligible, where + // + // v = beta * y_orig + alpha * conja(A_dense) * x + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &v ); + + bli_copyv( y_orig, &v ); + + bli_mkherm( a ); + bli_obj_set_struc( BLIS_GENERAL, a ); + bli_obj_set_uplo( BLIS_DENSE, a ); + + bli_gemv( alpha, a, x, beta, &v ); + + bli_subv( &v, y ); + bli_normfv( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &v ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_hemv.h b/gtestsuite/src/test_hemv.h new file mode 100644 index 000000000..d04d8bf15 --- /dev/null +++ b/gtestsuite/src/test_hemv.h @@ -0,0 +1,19 @@ +#ifndef TEST_HEMV_H +#define TEST_HEMV_H + +#include "blis_test.h" + +double libblis_test_ihemv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_hemv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_HEMV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_her.cpp b/gtestsuite/src/test_her.cpp new file mode 100644 index 000000000..a79133ec2 --- /dev/null +++ b/gtestsuite/src/test_her.cpp @@ -0,0 +1,436 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_her.h" + +// Local prototypes. +void libblis_test_her_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_her_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a +); + +double libblis_test_her_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +); + +void cblas_her( + uplo_t uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* a, + f77_int lda, + num_t dt +){ + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_ORDER cblas_order = CblasColMajor; + + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploa ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + cblas_ssyr(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + cblas_dsyr(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + case BLIS_SCOMPLEX : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cblas_cher(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + case BLIS_DCOMPLEX : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + cblas_zher(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_her( + f77_char f77_uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* a, + f77_int lda, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + ssyr_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + dsyr_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + case BLIS_SCOMPLEX : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cher_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + case BLIS_DCOMPLEX : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + zher_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_her( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_her_impl( iface, alpha, x, a ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if(params->api == API_CBLAS) { + cblas_her(uploa, mm, alpha, x, incx, a, lda, dt ); + } + else { /**/ + f77_char f77_uploa; + if ( bli_obj_row_stride( a ) == 1 ){ + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_her(f77_uploa, mm, alpha, x, incx, a, lda, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + conjugate_tensor(x, dt); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_her(f77_uploa, mm, alpha, x, incx, a, lda, dt ); + } + } + } + return ; +} + +double libblis_ref_her( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_her_check( params, alpha, x, a, a_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iher_check( params, alpha, x, a, a_orig ); + } + else { + resid = libblis_test_matrix_check(params, a); + } + } + return resid; +} + +double libblis_test_bitrp_her( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( a_orig, r ); + bli_mktrim( r ); + libblis_test_her_impl( iface, alpha, x, r ); + resid = libblis_test_bitrp_matrix(a, r, dt); + } + return resid; +} + +double libblis_test_op_her ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + conj_t conjx; + obj_t alpha, x, a; + obj_t a_save; + double resid = 0.0; + obj_t xx; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &xx ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, m, &a_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_HERMITIAN, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + bli_setsc( alpv.real, 0.0, &alpha ); + // Randomize x. + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_mobj_randomize( params, TRUE, &a ); + } + else{ + int32_t xx = (int32_t)alpv.real; + bli_setsc( (double)xx, (double)0.0, &alpha ); + // Randomize x. + libblis_test_vobj_irandomize( params, &x ); + libblis_test_mobj_irandomize( params, &a ); + } + + // Randomize A, make it densely Hermitian, and zero the unstored triangle + // to ensure the implementation is reads only from the stored region. + bli_mkherm( &a ); + bli_mktrim( &a ); + + // Save A and set its structure and uplo properties. + bli_obj_set_struc( BLIS_HERMITIAN, &a_save ); + bli_obj_set_uplo( uploa, &a_save ); + bli_copym( &a, &a_save ); + bli_mktrim( &a_save ); + + // Apply the remaining parameters. + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &x, &xx ); + + libblis_api_her(params, iface, &alpha, &xx, &a, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, m, &r ); + bli_obj_set_struc( BLIS_HERMITIAN, &r ); + bli_obj_set_uplo( uploa, &r ); + + resid = libblis_test_bitrp_her( params, iface, &alpha, &x, &a, &a_save, + &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_her( params, &alpha, &x, &a, &a_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &a, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &a ); + libblis_test_obj_free( &a_save ); + + return abs(resid); +} + +void libblis_test_her_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_her( alpha, x, a ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_her_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +){ + num_t dt = bli_obj_dt( a ); + num_t dt_real = bli_obj_dt_proj_to_real( a ); + + dim_t m_a = bli_obj_length( a ); + + obj_t xh, t, v, w; + obj_t rho, norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - a is randomized and Hermitian. + // Note: + // - alpha must be real-valued. + // + // Under these conditions, we assume that the implementation for + // + // A := A_orig + alpha * conjx(x) * conjx(x)^H + // + // is functioning correctly if + // + // normfv( v - w ) + // + // is negligible, where + // + // v = A * t + // w = ( A_orig + alpha * conjx(x) * conjx(x)^H ) * t + // = A_orig * t + alpha * conjx(x) * conjx(x)^H * t + // = A_orig * t + alpha * conjx(x) * rho + // = A_orig * t + w + // + + bli_mkherm( a ); + bli_mkherm( a_orig ); + bli_obj_set_struc( BLIS_GENERAL, a ); + bli_obj_set_struc( BLIS_GENERAL, a_orig ); + bli_obj_set_uplo( BLIS_DENSE, a ); + bli_obj_set_uplo( BLIS_DENSE, a_orig ); + + bli_obj_scalar_init_detached( dt, &rho ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m_a, 1, 0, 0, &t ); + bli_obj_create( dt, m_a, 1, 0, 0, &v ); + bli_obj_create( dt, m_a, 1, 0, 0, &w ); + + bli_obj_alias_with_conj( BLIS_CONJUGATE, x, &xh ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, a, &t, &BLIS_ZERO, &v ); + + bli_dotv( &xh, &t, &rho ); + bli_mulsc( alpha, &rho ); + bli_scal2v( &rho, x, &w ); + bli_gemv( &BLIS_ONE, a_orig, &t, &BLIS_ONE, &w ); + + bli_subv( &w, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_her.h b/gtestsuite/src/test_her.h new file mode 100644 index 000000000..1ebe673bb --- /dev/null +++ b/gtestsuite/src/test_her.h @@ -0,0 +1,17 @@ +#ifndef TEST_HER_H +#define TEST_HER_H + +#include "blis_test.h" + +double libblis_test_iher_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig + ); + +double libblis_check_nan_her( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_HER_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_her2.cpp b/gtestsuite/src/test_her2.cpp new file mode 100644 index 000000000..80fee07c3 --- /dev/null +++ b/gtestsuite/src/test_her2.cpp @@ -0,0 +1,497 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_her2.h" + +// Local prototypes. +void libblis_test_her2_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_her2_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a +); + +double libblis_test_her2_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +); + +void cblas_her2( + uplo_t uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* a, + f77_int lda, + num_t dt +){ + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_ORDER cblas_order; + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploa ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_ssyr2(cblas_order, cblas_uplo, m, *alphap, xp, incx, + yp, incy, ap, lda); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dsyr2(cblas_order, cblas_uplo, m, *alphap, xp, incx, + yp, incy, ap, lda); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_cher2(cblas_order, cblas_uplo, m, alphap, xp, incx, + yp, incy, ap, lda); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zher2(cblas_order, cblas_uplo, m, alphap, xp, incx, + yp, incy, ap, lda); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_her2( + f77_char f77_uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* a, + f77_int lda, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + ssyr2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + dsyr2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cher2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zher2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_her2( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_her2_impl( iface, alpha, x, y, a ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if(params->api == API_CBLAS) { + cblas_her2(uploa, mm, alpha, x, incx, y, incy, a, lda, dt ); + } + else { /**/ + f77_char f77_uploa; + if ( bli_obj_row_stride( a ) == 1 ){ + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_her2(f77_uploa, mm, alpha, x, incx, y, incy, a, lda, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + conjugate_tensor(x, dt); + conjugate_tensor(y, dt); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_her2(f77_uploa, mm, alpha, y, incy, x, incx, a, lda, dt ); + } + } + } + return ; +} + +double libblis_ref_her2( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_her2_check( params, alpha, x, y, a, a_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iher2_check( params, alpha, x, y, a, a_orig); + } + else { + resid = libblis_test_matrix_check(params, a); + } + } + return resid; +} + +double libblis_test_bitrp_her2( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( a_orig, r ); + bli_mkherm( r ); + bli_mktrim( r ); + libblis_test_her2_impl( iface, alpha, x, y, r ); + resid = libblis_test_bitrp_matrix(a, r, dt); + } + return resid; +} + +double libblis_test_op_her2 ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + conj_t conjx, conjy; + obj_t alpha, x, y, a; + obj_t a_save; + double resid = 0.0; + obj_t xx, yy; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[2], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &xx ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &yy ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, m, &a_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_HERMITIAN, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &x ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + } + else { + bli_setsc( alpv.real, alpv.imag, &alpha ); + } + // Randomize x and y. + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + libblis_test_mobj_randomize( params, TRUE, &a ); + } + else{ + int32_t xx = (int32_t)alpv.real; + if ( bli_obj_is_real( &x ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + } + else { + int32_t ax = (int32_t)(xx/0.8); + bli_setsc( (double)xx, (double)ax, &alpha ); + } + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + libblis_test_mobj_irandomize( params, &a ); + } + + // Randomize A, make it densely Hermitian, and zero the unstored triangle + // to ensure the implementation is reads only from the stored region. + bli_mkherm( &a ); + bli_mktrim( &a ); + + // Save A and set its structure and uplo properties. + bli_obj_set_struc( BLIS_HERMITIAN, &a_save ); + bli_obj_set_uplo( uploa, &a_save ); + bli_copym( &a, &a_save ); + bli_mkherm( &a_save ); + bli_mktrim( &a_save ); + + // Apply the remaining parameters. + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + bli_copyv( &x, &xx ); + bli_copyv( &y, &yy ); + + libblis_api_her2(params, iface, &alpha, &xx, &yy, &a, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, m, &r ); + bli_obj_set_struc( BLIS_HERMITIAN, &r ); + bli_obj_set_uplo( uploa, &r ); + + resid = libblis_test_bitrp_her2( params, iface, &alpha, &x, &y, + &a, &a_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_her2( params, &alpha, &x, &y, &a, &a_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &a, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &yy ); + libblis_test_obj_free( &a ); + libblis_test_obj_free( &a_save ); + + return abs(resid); +} + +void libblis_test_her2_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_her2( alpha, x, y, a ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_her2_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + num_t dt = bli_obj_dt( a ); + num_t dt_real = bli_obj_dt_proj_to_real( a ); + + dim_t m_a = bli_obj_length( a ); + + obj_t xh, yh, alphac; + obj_t t, v, w1, w2; + obj_t rho, norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y is randomized. + // - a is randomized and Hermitian. + // + // Under these conditions, we assume that the implementation for + // + // A := A_orig + alpha * conjx(x) * conjy(y)^H + conj(alpha) * conjy(y) * conjx(x)^H + // + // is functioning correctly if + // + // normfv( v - w ) + // + // is negligible, where + // + // v = A * t + // w = ( A_orig + alpha * conjx(x) * conjy(y)^H + conj(alpha) * conjy(y) * conjx(x)^H ) * t + // = A_orig * t + alpha * conjx(x) * conjy(y)^H * t + conj(alpha) * conjy(y) * conjx(x)^H * t + // = A_orig * t + alpha * conjx(x) * conjy(y)^H * t + conj(alpha) * conjy(y) * rho + // = A_orig * t + alpha * conjx(x) * conjy(y)^H * t + w1 + // = A_orig * t + alpha * conjx(x) * rho + w1 + // = A_orig * t + w2 + w1 + // + + bli_mkherm( a ); + bli_mkherm( a_orig ); + bli_obj_set_struc( BLIS_GENERAL, a ); + bli_obj_set_struc( BLIS_GENERAL, a_orig ); + bli_obj_set_uplo( BLIS_DENSE, a ); + bli_obj_set_uplo( BLIS_DENSE, a_orig ); + + bli_obj_scalar_init_detached( dt, &rho ); + bli_obj_scalar_init_detached( dt, &alphac ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m_a, 1, 0, 0, &t ); + bli_obj_create( dt, m_a, 1, 0, 0, &v ); + bli_obj_create( dt, m_a, 1, 0, 0, &w1 ); + bli_obj_create( dt, m_a, 1, 0, 0, &w2 ); + + bli_obj_alias_with_conj( BLIS_CONJUGATE, x, &xh ); + bli_obj_alias_with_conj( BLIS_CONJUGATE, y, &yh ); + bli_obj_alias_with_conj( BLIS_CONJUGATE, alpha, &alphac ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, a, &t, &BLIS_ZERO, &v ); + + bli_dotv( &xh, &t, &rho ); + bli_mulsc( &alphac, &rho ); + bli_scal2v( &rho, y, &w1 ); + + bli_dotv( &yh, &t, &rho ); + bli_mulsc( alpha, &rho ); + bli_scal2v( &rho, x, &w2 ); + + bli_addv( &w2, &w1 ); + + bli_gemv( &BLIS_ONE, a_orig, &t, &BLIS_ONE, &w1 ); + + bli_subv( &w1, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w1 ); + bli_obj_free( &w2 ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_her2.h b/gtestsuite/src/test_her2.h new file mode 100644 index 000000000..b11061689 --- /dev/null +++ b/gtestsuite/src/test_her2.h @@ -0,0 +1,18 @@ +#ifndef TEST_HER2_H +#define TEST_HER2_H + +#include "blis_test.h" + +double libblis_test_iher2_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig + ); + +double libblis_check_nan_her2( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_HER2_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_her2k.cpp b/gtestsuite/src/test_her2k.cpp new file mode 100644 index 000000000..1e1d62962 --- /dev/null +++ b/gtestsuite/src/test_her2k.cpp @@ -0,0 +1,554 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_her2k.h" + +using namespace std; + +// Local prototypes. +void libblis_test_her2k_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +); + +double libblis_test_her2k_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_her2k( + uplo_t uploc, + trans_t trans, + f77_int mm, + f77_int kk, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uploc; + enum CBLAS_TRANSPOSE cblas_trans; + + if( bli_is_trans( trans ) ) + cblas_trans = CblasConjTrans; + else + cblas_trans = CblasNoTrans; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if(bli_is_upper(uploc)) + cblas_uploc = CblasUpper; + else + cblas_uploc = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_ssyr2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dsyr2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_cher2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zher2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return 0; +} + +double blas_her2k( + uplo_t uploc, + trans_t trans, + f77_int mm, + f77_int kk, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + + f77_char f77_uploc; + f77_char f77_trans; + + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + bli_param_map_blis_to_netlib_trans( trans, &f77_trans ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + ssyr2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dsyr2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap,(f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cher2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zher2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_her2k( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_her2k_impl( iface, alpha, a, b, beta, c ); + } + else { /*CLBAS || BLAS */ + uplo_t uploc = bli_obj_uplo( c ); + dim_t m = bli_obj_length( c ); + dim_t k = bli_obj_width_after_trans( a ); + trans_t trans = bli_obj_onlytrans_status( a ); + f77_int lda, ldb, ldc; + + if( bli_obj_row_stride( c ) == 1 ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + ldc = ldc + params->ld[2]; + } + + if(params->api == API_CBLAS) { + cblas_her2k( uploc, trans, m, k, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } else { + if( bli_obj_row_stride( c ) == 1 ) { + blas_her2k( uploc, trans, m, k, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + else { + if( uploc == BLIS_UPPER) + uploc = BLIS_LOWER; + else if(uploc == BLIS_LOWER) + uploc = BLIS_UPPER; + + if( trans == BLIS_NO_TRANSPOSE) + trans = BLIS_TRANSPOSE; + else if(trans == BLIS_TRANSPOSE) + trans = BLIS_NO_TRANSPOSE; + + blas_her2k( uploc, trans, m, k, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + } + } + return ; +} + +double libblis_ref_her2k( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_save, + num_t dt +) { + + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_her2k(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_her2k_check( params, alpha, a, b, beta, c, c_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iher2k_check( params, alpha, a, b, beta, c, c_save ); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_her2k( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_her2k_impl( iface, alpha, a, b, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_her2k ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, k; + uplo_t uploc; + trans_t trans; + obj_t alpha, a, b, beta, c; + obj_t c_save; + double resid = 0.0; + obj_t aa, bb; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploc ); + bli_param_map_char_to_herk_trans( pc_str[1], &trans ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, trans, + sc_str[1], m, k, &a ); + libblis_test_mobj_create( params, datatype, trans, + sc_str[1], m, k, &aa ); + libblis_test_mobj_create( params, datatype, trans, + sc_str[2], m, k, &b ); + libblis_test_mobj_create( params, datatype, trans, + sc_str[2], m, k, &bb ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_save ); + + // Set the structure and uplo properties of C. + bli_obj_set_struc( BLIS_HERMITIAN, &c ); + bli_obj_set_uplo( uploc, &c ); + + // Set alpha and beta. + // For her2k, alpha may be complex, but beta must be real-valued + // (in order to preserve the Hermitian structure of C). + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + // Randomize A, B, and C, and save C. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_mobj_randomize( params, TRUE, &b ); + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)1.0; //alpv.real; + int32_t y = (int32_t)1.0; //betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + libblis_test_mobj_irandomize( params, &c ); + } + + if ((params->nanf) && (betv.real == 0) ) { + test_fillbuffmem(&c, datatype ); + } + + // Randomize A, make it densely Hermitian, and zero the unstored triangle + // to ensure the implementation is reads only from the stored region. + bli_mkherm( &c ); + bli_mktrim( &c ); + + // Save C and set its structure and uplo properties. + bli_obj_set_struc( BLIS_HERMITIAN, &c_save ); + bli_obj_set_uplo( uploc, &c_save ); + bli_copym( &c, &c_save ); + bli_mkherm( &c_save ); + bli_mktrim( &c_save ); + + // Apply the remaining parameters. + bli_copym( &a, &aa ); + bli_copym( &b, &bb ); + + bli_obj_set_conjtrans( trans, &a ); + bli_obj_set_conjtrans( trans, &b ); + + bli_obj_set_conjtrans( trans, &aa ); + bli_obj_set_conjtrans( trans, &bb ); + + libblis_api_her2k(params, iface, &alpha, &aa, &bb, &beta, &c, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &r ); + + resid = libblis_test_bitrp_her2k( params, iface, &alpha, &a, &b, + &beta, &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_her2k(params, &alpha, &a, &b, &beta, + &c, &c_save, datatype ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &bb ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_her2k_impl + ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c + ) +{ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_her2k( alpha, a, b, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_her2k_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t k = bli_obj_width_after_trans( a ); + + obj_t alphac, ah, bh; + obj_t norm; + obj_t t, v, w1, w2, z; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized. + // - b is randomized. + // - c_orig is randomized and Hermitian. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // - beta must be real-valued. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transb(B)^H + conj(alpha) * transb(B) * transa(A)^H + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // z = ( beta * C_orig + alpha * transa(A) * transb(B)^H + conj(alpha) * transb(B) * transa(A)^H ) * t + // = beta * C_orig * t + alpha * transa(A) * transb(B)^H * t + conj(alpha) * transb(B) * transa(A)^H * t + // = beta * C_orig * t + alpha * transa(A) * transb(B)^H * t + conj(alpha) * transb(B) * w2 + // = beta * C_orig * t + alpha * transa(A) * w1 + conj(alpha) * transb(B) * w2 + // = beta * C_orig * t + alpha * transa(A) * w1 + z + // = beta * C_orig * t + z + // + + bli_obj_alias_with_trans( BLIS_CONJ_TRANSPOSE, a, &ah ); + bli_obj_alias_with_trans( BLIS_CONJ_TRANSPOSE, b, &bh ); + + bli_obj_scalar_init_detached( dt_real, &norm ); + bli_obj_scalar_init_detached_copy_of( dt, BLIS_CONJUGATE, alpha, &alphac ); + + bli_obj_create( dt, m, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, k, 1, 0, 0, &w1 ); + bli_obj_create( dt, k, 1, 0, 0, &w2 ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_hemv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + bli_gemv( &BLIS_ONE, &ah, &t, &BLIS_ZERO, &w2 ); + bli_gemv( &BLIS_ONE, &bh, &t, &BLIS_ZERO, &w1 ); + bli_gemv( alpha, a, &w1, &BLIS_ZERO, &z ); + bli_gemv( &alphac, b, &w2, &BLIS_ONE, &z ); + bli_hemv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w1 ); + bli_obj_free( &w2 ); + bli_obj_free( &z ); + + return resid; +} + diff --git a/gtestsuite/src/test_her2k.h b/gtestsuite/src/test_her2k.h new file mode 100644 index 000000000..cd5759728 --- /dev/null +++ b/gtestsuite/src/test_her2k.h @@ -0,0 +1,19 @@ +#ifndef TEST_HER2K_H +#define TEST_HER2K_H + +#include "blis_test.h" + +double libblis_test_iher2k_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_her2k(obj_t* c, num_t dt ); + +#endif /* TEST_HER2K_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_herk.cpp b/gtestsuite/src/test_herk.cpp new file mode 100644 index 000000000..396a1be3c --- /dev/null +++ b/gtestsuite/src/test_herk.cpp @@ -0,0 +1,505 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_herk.h" + +void libblis_test_herk_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c +); + +double libblis_test_herk_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_herk( + uplo_t uploc, + trans_t transa, + f77_int n, + f77_int k, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_TRANSPOSE cblas_trans; + enum CBLAS_UPLO cblas_uplo; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploc ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if( bli_is_trans( transa ) ) + cblas_trans = CblasConjTrans; + else + cblas_trans = CblasNoTrans; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_ssyrk( cblas_order, cblas_uplo, cblas_trans, n, k, + *alphap, ap, lda, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dsyrk( cblas_order, cblas_uplo, cblas_trans, n, k, + *alphap, ap, lda, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + float* betap = (float*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_cherk( cblas_order, cblas_uplo, cblas_trans, n, k, + *alphap, ap, lda, *betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + double* betap = (double*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zherk( cblas_order, cblas_uplo, cblas_trans, n, k, + *alphap, ap, lda, *betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_herk( + uplo_t uploc, + f77_char f77_transa, + f77_int n, + f77_int k, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + f77_char f77_uploc; + + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + ssyrk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, (f77_int*)&lda, + betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dsyrk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, + (f77_int*)&lda, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + float* betap = (float*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cherk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, + (f77_int*)&lda, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + double* betap = (double*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zherk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, + (f77_int*)&lda, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_herk( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_herk_impl( iface, alpha, a, beta, c ); + } + else { /*CLBAS || BLAS */ + f77_int m = bli_obj_length( c ); + f77_int k = bli_obj_width_after_trans( a ); + uplo_t uploc = bli_obj_uplo( c ); + trans_t transa = bli_obj_onlytrans_status( a ); + f77_int lda, ldc; + + if( bli_obj_is_col_stored( c ) ) { + lda = bli_obj_col_stride( a ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldc = ldc + params->ld[2]; + } + + if(params->api == API_CBLAS) { + cblas_herk(uploc, transa, m, k, alpha, a, lda, beta, c, ldc, dt); + } + else { /**/ + f77_char f77_transa; + if(bli_obj_is_col_stored( c )) { + if(transa == BLIS_TRANSPOSE) f77_transa='T'; + else if ( transa == BLIS_CONJ_TRANSPOSE ) f77_transa='C'; + else /*if ( transa == BLIS_NO_TRANSPOSE )*/ f77_transa='N'; + + blas_herk(uploc, f77_transa, m, k, alpha, a, lda, + beta, c, ldc, dt); + } + else { + if(transa == BLIS_TRANSPOSE) f77_transa='N'; + else if ( transa == BLIS_CONJ_TRANSPOSE ) f77_transa='N'; + else /*if ( transa == BLIS_NO_TRANSPOSE )*/ f77_transa='C'; + + if( uploc == BLIS_UPPER) + uploc = BLIS_LOWER; + else if(uploc == BLIS_LOWER) + uploc = BLIS_UPPER; + + blas_herk(uploc, f77_transa, m, k, alpha, a, lda, + beta, c, ldc, dt); + } + } + } + return ; +} + +double libblis_ref_herk( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + num_t dt +){ + double resid = 0.0; +// double *betap = (double *)bli_obj_buffer( beta ); + + if (params->nanf) { + resid = libblis_check_nan_herk(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_herk_check( params, alpha, a, beta, c, c_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iherk_check( params, alpha, a, beta, c, c_orig); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_herk( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_herk_impl( iface, alpha, a, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_herk ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m, k; + uplo_t uploc; + trans_t transa; + obj_t alpha, a, beta, c; + obj_t c_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploc ); + bli_param_map_char_to_herk_trans( pc_str[1], &transa ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], m, k, &a ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], m, k, &aa ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_save ); + + // Set the structure and uplo properties of C. + bli_obj_set_struc( BLIS_HERMITIAN, &c ); + bli_obj_set_uplo( uploc, &c ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc(alpv.real, 0.0, &alpha); + bli_setsc(betv.real, 0.0, &beta); + } + else { + // For herk, alpha and beta must both be real-valued, even in the + // complex case (in order to preserve the Hermitian structure of C). + bli_setsc(alpv.real, 0.0, &alpha); + bli_setsc(betv.real, 0.0, &beta); + } + // Randomize A. + libblis_test_mobj_randomize( params, TRUE, &a ); + + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)alpv.real; + int32_t y = (int32_t)betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + // For herk, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + // Randomize A. + libblis_test_mobj_irandomize( params, &a ); + + libblis_test_mobj_irandomize( params, &c ); + } + + bli_mkherm( &c ); + bli_mktrim( &c ); + + // Save C and set its structure and uplo properties. + bli_obj_set_struc( BLIS_HERMITIAN, &c_save ); + bli_obj_set_uplo( uploc, &c_save ); + bli_copym( &c, &c_save ); + + bli_mkherm( &c_save ); + bli_mktrim( &c_save ); + + bli_copym( &a, &aa ); + + // Apply the remaining parameters. + bli_obj_set_conjtrans( transa, &a ); + + bli_obj_set_conjtrans( transa, &aa ); + + libblis_api_herk(params, iface, &alpha, &aa, &beta, &c, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &r ); + resid = libblis_test_bitrp_herk( params, iface, &alpha, &a, &beta, + &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_herk(params, &alpha, &a, &beta, &c, &c_save, datatype); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_herk_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c +) +{ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_herk( alpha, a, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_herk_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t k = bli_obj_width_after_trans( a ); + + obj_t ah; + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized. + // - c_orig is randomized and Hermitian. + // Note: + // - alpha and beta must be real-valued. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transa(A)^H + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // z = ( beta * C_orig + alpha * transa(A) * transa(A)^H ) * t + // = beta * C_orig * t + alpha * transa(A) * transa(A)^H * t + // = beta * C_orig * t + alpha * transa(A) * w + // = beta * C_orig * t + z + // + + bli_obj_alias_with_trans( BLIS_CONJ_TRANSPOSE, a, &ah ); + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, k, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_hemv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + bli_gemv( &BLIS_ONE, &ah, &t, &BLIS_ZERO, &w ); + bli_gemv( alpha, a, &w, &BLIS_ZERO, &z ); + bli_hemv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_herk.h b/gtestsuite/src/test_herk.h new file mode 100644 index 000000000..48934ec65 --- /dev/null +++ b/gtestsuite/src/test_herk.h @@ -0,0 +1,18 @@ +#ifndef TEST_HERK_H +#define TEST_HERK_H + +#include "blis_test.h" + +double libblis_test_iherk_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_herk(obj_t* b, num_t dt ); + +#endif /* TEST_HERK_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_normfm.cpp b/gtestsuite/src/test_normfm.cpp new file mode 100644 index 000000000..1c0af239a --- /dev/null +++ b/gtestsuite/src/test_normfm.cpp @@ -0,0 +1,199 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_normfm.h" + +// Local prototypes. +void libblis_test_normfm_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_normfm_impl( + iface_t iface, + obj_t* x, + obj_t* norm +); + +double libblis_test_normfm_check( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* norm +); + +double libblis_ref_normfm( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* norm +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_normfm_check( params, beta, x, norm ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_inormfm_check( params, x, norm); + } + else { + resid = libblis_test_matrix_check(params, x); + } + } + return resid; +} + +double libblis_test_bitrp_normfm( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* norm, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + num_t dt_real = bli_dt_proj_to_real( dt ); + + for(i = 0; i < n_repeats; i++) { + bli_obj_scalar_init_detached( dt_real, r ); + libblis_test_normfm_impl( iface, x, r ); + resid = libblis_test_bitrp_matrix(norm, r, dt); + } + return resid; +} + +double libblis_test_op_normfm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + num_t dt_real; + dim_t m, n; + obj_t beta, norm; + obj_t x; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Compute the real projection of the chosen datatype. + dt_real = bli_dt_proj_to_real( datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &x ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Initialize beta to 2 - 2i. + bli_setsc( 2.0, -2.0, &beta ); + // Set all elements of x to beta. + bli_setm( &beta, &x ); + } + else { + libblis_test_mobj_irandomize( params, &x ); + } + + libblis_test_normfm_impl( iface, &x, &norm ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + resid = libblis_test_bitrp_normfm( params, iface, &x, &norm, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_normfm( params, &beta, &x, &norm ); + } +#endif + + // Zero out performance and residual if input matrix is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_normfm_impl( + iface_t iface, + obj_t* x, + obj_t* norm +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_normfm( x, norm ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_normfm_check ( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* norm +){ + num_t dt_real = bli_obj_dt_proj_to_real( x ); + dim_t m = bli_obj_length( x ); + dim_t n = bli_obj_width( x ); + + obj_t m_r, n_r, temp_r; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is set to beta. + // Note: + // - beta should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // norm := normfm( x ) + // + // is functioning correctly if + // + // norm = sqrt( absqsc( beta ) * m * n ) + // + // where m and n are the dimensions of x. + // + + bli_obj_scalar_init_detached( dt_real, &temp_r ); + bli_obj_scalar_init_detached( dt_real, &m_r ); + bli_obj_scalar_init_detached( dt_real, &n_r ); + + bli_setsc( ( double )m, 0.0, &m_r ); + bli_setsc( ( double )n, 0.0, &n_r ); + + bli_absqsc( beta, &temp_r ); + bli_mulsc( &m_r, &temp_r ); + bli_mulsc( &n_r, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + bli_subsc( &temp_r, norm ); + + bli_getsc( norm, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_normfm.h b/gtestsuite/src/test_normfm.h new file mode 100644 index 000000000..a32f21f83 --- /dev/null +++ b/gtestsuite/src/test_normfm.h @@ -0,0 +1,15 @@ +#ifndef TEST_NORMFM_H +#define TEST_NORMFM_H + +#include "blis_test.h" + +double libblis_test_inormfm_check + ( + test_params_t* params, + obj_t* x, + obj_t* n + ); + +double libblis_check_nan_normfm( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_NORMFM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_normfv.cpp b/gtestsuite/src/test_normfv.cpp new file mode 100644 index 000000000..ac2bf7a0b --- /dev/null +++ b/gtestsuite/src/test_normfv.cpp @@ -0,0 +1,296 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_normfv.h" + +// Local prototypes. +void libblis_test_normfv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_normfv_impl ( + iface_t iface, + obj_t* x, + obj_t* norm +); + +double libblis_test_normfv_check ( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* norm +); + +double cblas_normfv( + f77_int mm, + obj_t* x, + f77_int incx, + obj_t* norm, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* nrm2 = (float*) bli_obj_buffer( norm ); + float* xp = (float*) bli_obj_buffer( x ); + *nrm2 = cblas_snrm2( mm, xp, incx ); + break; + } + case BLIS_DOUBLE : + { + double* nrm2 = (double*) bli_obj_buffer( norm ); + double* xp = (double*) bli_obj_buffer( x ); + *nrm2 = cblas_dnrm2( mm, xp, incx ); + break; + } + case BLIS_SCOMPLEX : + { + float* nrm2 = (float*) bli_obj_buffer( norm ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + *nrm2 = cblas_scnrm2( mm, xp, incx ); + break; + } + case BLIS_DCOMPLEX : + { + double* nrm2 = (double*) bli_obj_buffer( norm ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + *nrm2 = cblas_dznrm2( mm, xp, incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_normfv( + f77_int mm, + obj_t* x, + f77_int incx, + obj_t* norm, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* nrm2 = (float*) bli_obj_buffer( norm ); + float* xp = (float*) bli_obj_buffer( x ); + *nrm2 = snrm2_( &mm, xp, &incx ); + break; + } + case BLIS_DOUBLE : + { + double* nrm2 = (double*) bli_obj_buffer( norm ); + double* xp = (double*) bli_obj_buffer( x ); + *nrm2 = dnrm2_( &mm, xp, &incx ); + break; + } + case BLIS_SCOMPLEX : + { + float* nrm2 = (float*) bli_obj_buffer( norm ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + *nrm2 = scnrm2_( &mm, xp, &incx ); + break; + } + case BLIS_DCOMPLEX : + { + double* nrm2 = (double*) bli_obj_buffer( norm ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + *nrm2 = dznrm2_( &mm, xp, &incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_normfv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* norm, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_normfv_impl( iface, x, norm ); + } + else { /*CLBAS || BLAS */ + f77_int m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + + if( params->api == API_CBLAS ) { + cblas_normfv( m, x, incx, norm, dt ); + } else { + blas_normfv( m, x, incx, norm, dt ); + } + } + return ; +} + +double libblis_ref_normfv( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* norm +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_normfv_check( params, beta, x, norm); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_inormfv_check( params, beta, x, norm); + } + else { + resid = libblis_test_vector_check(params, x); + } + } + return resid; +} + +double libblis_test_bitrp_normfv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* norm, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + num_t dt_real = bli_dt_proj_to_real( dt ); + + for(i = 0; i < n_repeats; i++) { + bli_obj_scalar_init_detached( dt_real, r ); + libblis_test_normfv_impl( iface, x, r ); + resid = libblis_test_bitrp_vector(norm, r, dt); + } + return resid; +} + +double libblis_test_op_normfv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +) { + num_t datatype; + num_t dt_real; + dim_t m; + obj_t beta, norm; + obj_t x; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Compute the real projection of the chosen datatype. + dt_real = bli_dt_proj_to_real( datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + + // Initialize beta to 2 - 2i. + bli_setsc( 2.0, -2.0, &beta ); + + // Set all elements of x to beta. + bli_setv( &beta, &x ); + + libblis_api_normfv( params, iface, &x, &norm, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + resid = libblis_test_bitrp_normfv( params, iface, &x, &norm, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_normfv( params, &beta, &x, &norm); + } +#endif + + // Zero out performance and residual if input vector is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_normfv_impl ( + iface_t iface, + obj_t* x, + obj_t* norm +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_normfv( x, norm ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_normfv_check ( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* norm +) { + num_t dt_real = bli_obj_dt_proj_to_real( x ); + dim_t m = bli_obj_vector_dim( x ); + + obj_t m_r, temp_r; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is set to beta. + // Note: + // - beta should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // norm := normfv( x ) + // + // is functioning correctly if + // + // norm = sqrt( absqsc( beta ) * m ) + // + // where m is the length of x. + // + + bli_obj_scalar_init_detached( dt_real, &temp_r ); + bli_obj_scalar_init_detached( dt_real, &m_r ); + + bli_setsc( ( double )m, 0.0, &m_r ); + + bli_absqsc( beta, &temp_r ); + bli_mulsc( &m_r, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + bli_subsc( &temp_r, norm ); + + bli_getsc( norm, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_normfv.h b/gtestsuite/src/test_normfv.h new file mode 100644 index 000000000..c0e708c90 --- /dev/null +++ b/gtestsuite/src/test_normfv.h @@ -0,0 +1,16 @@ +#ifndef TEST_NORMFV_H +#define TEST_NORMFV_H + +#include "blis_test.h" + +double libblis_test_inormfv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* n + ); + +double libblis_check_nan_normfv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_NORMFV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_process.cpp b/gtestsuite/src/test_process.cpp new file mode 100644 index 000000000..2b928ef57 --- /dev/null +++ b/gtestsuite/src/test_process.cpp @@ -0,0 +1,1737 @@ +#include + +#include "blis_utils.h" +#include "blis_api.h" + +using namespace std; + +char libblis_test_sp_chars[ 2 + 1 ] = "sc"; +char libblis_test_dp_chars[ 2 + 1 ] = "dz"; + +char libblis_test_rd_chars[ 2 + 1 ] = "sd"; +char libblis_test_cd_chars[ 2 + 1 ] = "cz"; + +char libblis_test_dt_chars[ 4 + 1 ] = "sdcz"; + +char libblis_test_store_chars[ NUM_OPERAND_TYPES ][ MAX_STORE_VALS_PER_TYPE + 1 ]; +char libblis_test_param_chars[ NUM_PARAM_TYPES ][ MAX_PARAM_VALS_PER_TYPE + 1 ]; + +char libblis_test_pass_string[ MAX_PASS_STRING_LENGTH + 1 ]; +char libblis_test_warn_string[ MAX_PASS_STRING_LENGTH + 1 ]; +char libblis_test_fail_string[ MAX_PASS_STRING_LENGTH + 1 ]; +char libblis_test_overflow_string[ MAX_PASS_STRING_LENGTH + 1 ]; +char libblis_test_underflow_string[ MAX_PASS_STRING_LENGTH + 1 ]; + +#define SIGN(number) (number > 0 ? 1 : 0) +int BlisTestSuite::libblis_test_init_strings( blis_string_t *test_data ) +{ + // Copy default parameters filename into its global string. + strncpy( test_data->libblis_test_parameters_filename, + PARAMETERS_FILENAME, MAX_FILENAME_LENGTH ); + + // Copy default operations filename into its global string. + strncpy( test_data->libblis_test_operations_filename, + OPERATIONS_FILENAME, MAX_FILENAME_LENGTH ); + + // Copy default alpha-beta parameter filename . + strncpy( test_data->libblis_test_alphabeta_parameter, + ALPHA_BETA_FILENAME, MAX_FILENAME_LENGTH ); + + strcpy( libblis_test_pass_string, BLIS_TEST_PASS_STRING ); + strcpy( libblis_test_warn_string, BLIS_TEST_WARN_STRING ); + strcpy( libblis_test_fail_string, BLIS_TEST_FAIL_STRING ); + strcpy( libblis_test_overflow_string, BLIS_TEST_OVERFLOW_STRING ); + strcpy( libblis_test_underflow_string, BLIS_TEST_UNDERFLOW_STRING ); + + strcpy( libblis_test_param_chars[BLIS_TEST_PARAM_SIDE], BLIS_TEST_PARAM_SIDE_CHARS ); + strcpy( libblis_test_param_chars[BLIS_TEST_PARAM_UPLO], BLIS_TEST_PARAM_UPLO_CHARS ); + strcpy( libblis_test_param_chars[BLIS_TEST_PARAM_UPLODE], BLIS_TEST_PARAM_UPLODE_CHARS ); + strcpy( libblis_test_param_chars[BLIS_TEST_PARAM_TRANS], BLIS_TEST_PARAM_TRANS_CHARS ); + strcpy( libblis_test_param_chars[BLIS_TEST_PARAM_CONJ], BLIS_TEST_PARAM_CONJ_CHARS ); + strcpy( libblis_test_param_chars[BLIS_TEST_PARAM_DIAG], BLIS_TEST_PARAM_DIAG_CHARS ); + + strcpy( libblis_test_store_chars[BLIS_TEST_MATRIX_OPERAND], BLIS_TEST_MSTORE_CHARS ); + strcpy( libblis_test_store_chars[BLIS_TEST_VECTOR_OPERAND], BLIS_TEST_VSTORE_CHARS ); + + return 0; +} + +int BlisTestSuite::libblis_test_read_params_file + ( + char* input_filename, + test_params_t* params, + char* abpf + ) +{ + FILE* input_stream; + char buffer[ INPUT_BUFFER_SIZE ]; + char temp[ INPUT_BUFFER_SIZE ]; + unsigned int i; + + // Attempt to open input file corresponding to input_filename as + // read-only/binary. + input_stream = fopen( input_filename, "r" ); + libblis_test_fopen_check_stream( input_filename, input_stream ); + + // Read the number of repeats. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->n_repeats) ); + + // Read the matrix storage schemes to test. We should have at most three: + // 'r' for row-major, 'c' for column-major, and 'g' for general strides. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%s ", temp ); + + params->n_mstorage = strlen( temp ); + if( params->n_mstorage > MAX_NUM_MSTORAGE ) + { + printf( "Detected too many matrix storage schemes (%u) in input file.\n", + params->n_mstorage ); + } + strcpy( params->storage[ BLIS_TEST_MATRIX_OPERAND ], temp ); + + // Read the vector storage schemes to test. We should have at most four: + // 'r' for row vectors with unit stride, 'c' for column vectors with unit + // stride, 'i' for row vectors with non-unit stride, and 'j' for column + // vectors with non-unit stride. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%s ", temp ); + + params->n_vstorage = strlen( temp ); + if( params->n_vstorage > MAX_NUM_VSTORAGE ) + { + printf( "Detected too many vector storage schemes (%u) in input file.\n", + params->n_vstorage ); + } + strcpy( params->storage[ BLIS_TEST_VECTOR_OPERAND ], temp ); + + // Read whether to mix all storage combinations. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->mix_all_storage) ); + + // Read whether to perform all tests with aligned addresses and ldims. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->alignment) ); + +#ifdef __GTEST_VALGRIND_TEST__ + params->alignment = 0; +#endif + + // Read the randomization method. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->rand_method) ); + + if( params->rand_method != BLIS_TEST_RAND_REAL_VALUES && + params->rand_method != BLIS_TEST_RAND_NARROW_POW2 ) + { + printf( "Invalid randomization method (%u) in input file.\n", + params->rand_method ); + } + + // Read the general stride "spacing". + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->gs_spacing) ); + + // Overwrite the existing storage character arrays with the sets provided. + strcpy( libblis_test_store_chars[BLIS_TEST_MATRIX_OPERAND], + params->storage[BLIS_TEST_MATRIX_OPERAND] ); + strcpy( libblis_test_store_chars[BLIS_TEST_VECTOR_OPERAND], + params->storage[BLIS_TEST_VECTOR_OPERAND] ); + + // Read the datatypes to test. We should have at most four: 's', 'd', 'c', + // and 'z'. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%s ", temp ); + + params->n_datatypes = strlen( temp ); + if( params->n_datatypes > MAX_NUM_DATATYPES ) + { + printf( "Detected too many datatype requests (%u) in input file.\n", + params->n_datatypes ); + } + + for( i = 0 ; i < params->n_datatypes ; ++i ) + { + // Map the char in temp[i] to the corresponding num_t value. + bli_param_map_char_to_blis_dt( temp[i], &(params->datatype[i]) ); + params->datatype_char[i] = temp[i]; + } + + // Read whether to test gemm with mixed-domain operands. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->mixed_domain) ); + + // Read whether to test gemm with mixed-precision operands. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->mixed_precision) ); + + // Read the initial problem size to test. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->p_first) ); + + // Read the maximum problem size to test. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->p_max) ); + + // Read the problem size increment to test. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->p_inc) ); + + // Read whether to enable 3mh. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_3MH ]) ); + + // Read whether to enable 3m1. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_3M1 ]) ); + + // Read whether to enable 4mh. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_4MH ]) ); + + // Read whether to enable 4m1b (4mb). + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_4M1B ]) ); + + // Read whether to enable 4m1a (4m1). + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_4M1A ]) ); + + // Read whether to enable 1m. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_1M ]) ); + + // Read whether to native (complex) execution. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->ind_enable[ BLIS_NAT ]) ); + + // Read whether to simulate application-level threading. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->n_app_threads) ); + + // Silently interpret non-positive numbers the same as 1. + if( params->n_app_threads < 1 ) params->n_app_threads = 1; + + // Disable induced methods when simulating more than one application + // threads. + if( params->n_app_threads > 1 ) + { + if( params->ind_enable[ BLIS_3MH ] || + params->ind_enable[ BLIS_3M1 ] || + params->ind_enable[ BLIS_4MH ] || + params->ind_enable[ BLIS_4M1B ] || + params->ind_enable[ BLIS_4M1A ] || + params->ind_enable[ BLIS_1M ] ) + { + // Due to an inherent race condition in the way induced methods + // are enabled and disabled at runtime, all induced methods must be + // disabled when simulating multiple application threads. + printf( "simulating multiple application threads; disabling induced methods.\n" ); + + params->ind_enable[ BLIS_3MH ] = 0; + params->ind_enable[ BLIS_3M1 ] = 0; + params->ind_enable[ BLIS_4MH ] = 0; + params->ind_enable[ BLIS_4M1B ] = 0; + params->ind_enable[ BLIS_4M1A ] = 0; + params->ind_enable[ BLIS_1M ] = 0; + } + } + + // Read the requested error-checking level. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->error_checking_level) ); + + // Read the requested course of action if a test fails. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%c ", &(params->reaction_to_failure) ); + + if( params->reaction_to_failure != ON_FAILURE_IGNORE_CHAR && + params->reaction_to_failure != ON_FAILURE_SLEEP_CHAR && + params->reaction_to_failure != ON_FAILURE_ABORT_CHAR ) + { + printf( "Invalid reaction-to-failure character code (%c) in input file.\n", + params->reaction_to_failure ); + } + + // Read whether to output in matlab format. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->output_matlab_format) ); + + // Read whether to output to files in addition to stdout. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &(params->output_files) ); + + // Read the requested api path. + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", &i ); + params->api = (api_t)i; + + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", ¶ms->dimf ); + + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", ¶ms->abf ); + + { + FILE *input_abpf = fopen( abpf, "r" ); + libblis_test_fopen_check_stream( abpf, input_abpf ); + + libblis_test_read_next_line( buffer, input_abpf ); + sscanf( buffer, "%u ", ¶ms->nab ); + + params->alpha = ( atom_t * ) malloc( params->nab * sizeof( atom_t ) ); + params->beta = ( atom_t * ) malloc( params->nab * sizeof( atom_t ) ); + + for( i = 0 ; i < params->nab ; i++) + { + libblis_test_read_next_line( buffer, input_abpf ); + sscanf( buffer, "%lf %lf", ¶ms->alpha[i].real, ¶ms->alpha[i].imag ); + } + + for( i = 0 ; i < params->nab ; i++) + { + libblis_test_read_next_line( buffer, input_abpf ); + sscanf( buffer, "%lf %lf", ¶ms->beta[i].real, ¶ms->beta[i].imag); + } + fclose(input_abpf); + } + + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", ¶ms->bitextf ); + + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", ¶ms->passflag ); + + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%u ", ¶ms->bitrp ); + + libblis_test_read_next_line( buffer, input_stream ); + sscanf( buffer, "%c ", &(params->op_t) ); + + // Close the file. + fclose( input_stream ); + + if(params->oruflw != 0) + params->bitextf = 1; + + // Output the parameter struct. + libblis_test_output_params_struct( stdout, params ); + return 0; +} + +int BlisTestSuite::libblis_test_read_ops_file( char* input_filename, test_ops_t* ops ) +{ + FILE* input_stream; + + // Attempt to open input file corresponding to input_filename as + // read-only/binary. + input_stream = fopen( input_filename, "r" ); + libblis_test_fopen_check_stream( input_filename, input_stream ); + + // Initialize the individual override field to FALSE. + ops->indiv_over = FALSE; + + // Begin reading operations input file. +#if 0 + // Section overrides + libblis_test_read_section_override( ops, input_stream, &(ops->util_over) ); + libblis_test_read_section_override( ops, input_stream, &(ops->l1v_over) ); + libblis_test_read_section_override( ops, input_stream, &(ops->l1m_over) ); + libblis_test_read_section_override( ops, input_stream, &(ops->l1f_over) ); + libblis_test_read_section_override( ops, input_stream, &(ops->l2_over) ); + libblis_test_read_section_override( ops, input_stream, &(ops->l3ukr_over) ); + libblis_test_read_section_override( ops, input_stream, &(ops->l3_over) ); +#endif + // dimensions n_param operation + + // Utility operations + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->randv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 0, &(ops->randm) ); + + // Level-1v + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->addv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->amaxv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->axpbyv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->axpyv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->copyv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->dotv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->dotxv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->normfv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->scalv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->scal2v) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 0, &(ops->setv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->subv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 1, &(ops->xpbyv) ); + + // Level-1m + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->addm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->axpym) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->copym) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 0, &(ops->normfm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->scalm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->scal2m) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 0, &(ops->setm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->subm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 1, &(ops->xpbym) ); + + // Level-1f + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->axpy2v) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->dotaxpyv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MF, 2, &(ops->axpyf) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MF, 2, &(ops->dotxf) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MF, 4, &(ops->dotxaxpyf) ); + + // Level-2 + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 2, &(ops->gemv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MN, 2, &(ops->ger) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->hemv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->her) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->her2) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->symv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 2, &(ops->syr) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->syr2) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->trmv) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_M, 3, &(ops->trsv) ); + + // Level-3 micro-kernels + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_K, 0, &(ops->gemm_ukr) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_NO_DIMS, 1, &(ops->trsm_ukr) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_K, 1, &(ops->gemmtrsm_ukr) ); + + // Level-3 + libblis_test_read_op_info( ops, input_stream, BLIS_GEMM, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_GEMMT, BLIS_TEST_DIMS_MK, 3, &(ops->gemmt) ); + libblis_test_read_op_info( ops, input_stream, BLIS_HEMM, BLIS_TEST_DIMS_MN, 2, &(ops->hemm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_HERK, BLIS_TEST_DIMS_MK, 2, &(ops->herk) ); + libblis_test_read_op_info( ops, input_stream, BLIS_HER2K, BLIS_TEST_DIMS_MK, 2, &(ops->her2k) ); + libblis_test_read_op_info( ops, input_stream, BLIS_SYMM, BLIS_TEST_DIMS_MN, 2, &(ops->symm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_SYRK, BLIS_TEST_DIMS_MK, 2, &(ops->syrk) ); + libblis_test_read_op_info( ops, input_stream, BLIS_SYR2K, BLIS_TEST_DIMS_MK, 2, &(ops->syr2k) ); + libblis_test_read_op_info( ops, input_stream, BLIS_TRMM, BLIS_TEST_DIMS_MN, 4, &(ops->trmm) ); + libblis_test_read_op_info( ops, input_stream, BLIS_TRMM3, BLIS_TEST_DIMS_MN, 5, &(ops->trmm3) ); + libblis_test_read_op_info( ops, input_stream, BLIS_TRSM, BLIS_TEST_DIMS_MN, 4, &(ops->trsm) ); + + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s32os32) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s32os8) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_f32f32f32of32) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s16os16) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_u8s8s16os8) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_bf16bf16f32of32) ); + libblis_test_read_op_info( ops, input_stream, BLIS_NOID, BLIS_TEST_DIMS_MNK, 2, &(ops->gemm_bf16bf16f32obf16) ); + + // Output the section overrides. + libblis_test_output_section_overrides( stdout, ops ); + + // Close the file. + fclose( input_stream ); + + return 0; +} + +ind_t ind_enable_get_str( test_params_t* params, unsigned int d, + unsigned int x, test_op_t* op ) +{ + ind_t indi = (ind_t)params->indim[d][x]; + num_t datatype = params->dt[d]; + + bli_ind_oper_enable_only( op->opid, indi, datatype ); + + return bli_ind_oper_find_avail( op->opid, datatype ); +} + +void libblis_test_build_function_string + ( + char* prefix_str, + opid_t opid, + ind_t method, + char* ind_str, + const char* op_str, + unsigned int is_mixed_dt, + char* dc_str, + unsigned int n_param_combos, + char* pc_str, + char* sc_str, + char* funcname_str + ) +{ + // We only print the full datatype combination string if is_mixed_dt + // is set and the operation is gemm. Otherwise, we print only + // the first char (since they are all the same). + if( is_mixed_dt == TRUE ) //&& opid == BLIS_GEMM ) + sprintf( funcname_str, "%s_%s%s", prefix_str, dc_str, op_str ); + else + sprintf( funcname_str, "%s_%c%s", prefix_str, dc_str[0], op_str ); + + // If the method is non-native (ie: induced), append a string + // identifying the induced method. + if( method != BLIS_NAT ) + sprintf( &funcname_str[strlen(funcname_str)], "%s", ind_str ); + + // We check the string length of pc_str in case the user is running an + // operation that has parameters (and thus generally more than one + // parameter combination), but has fixed all parameters in the input + // file, which would result in n_param_combos to equal one. This way, + // the function string contains the parameter string associated with + // the parameters that were fixed. + if( n_param_combos > 1 || strlen( pc_str ) > 0 ) + sprintf( &funcname_str[strlen(funcname_str)], "_%s_%s", pc_str, sc_str ); + else + sprintf( &funcname_str[strlen(funcname_str)], "_%s", sc_str ); + + if( strlen( funcname_str ) > MAX_FUNC_STRING_LENGTH ) + libblis_test_printf_error( "Function name string length (%d) exceeds maximum (%d).\n", + strlen( funcname_str ), MAX_FUNC_STRING_LENGTH ); + return; +} + +// % dtoper_params_storage m n k gflops resid result +void libblis_test_build_col_labels_string + ( + test_params_t* params, + test_op_t* op, + char* l_str + ) +{ + unsigned int n_spaces; + char blank_str[64]; + + strcpy( l_str, "" ); + + if( op->n_params > 0 ) + { + sprintf( &l_str[strlen(l_str)], "%c %s_%s", OUTPUT_COMMENT_CHAR, + BLIS_FILEDATA_PREFIX_STR, + "

__" ); + } + else // ( n_params == 0 ) + { + sprintf( &l_str[strlen(l_str)], "%c %s_%s", OUTPUT_COMMENT_CHAR, + BLIS_FILEDATA_PREFIX_STR, + "
_ " ); + } + + if( params->output_matlab_format ) n_spaces = 11; + else n_spaces = 1; + + fill_string_with_n_spaces( blank_str, n_spaces ); + + sprintf( &l_str[strlen(l_str)], "%s", blank_str ); + + if( op->dimset == BLIS_TEST_DIMS_MNK || + op->dimset == BLIS_TEST_DIMS_MN || + op->dimset == BLIS_TEST_DIMS_MK || + op->dimset == BLIS_TEST_DIMS_M || + op->dimset == BLIS_TEST_DIMS_K || + op->dimset == BLIS_TEST_DIMS_MF || + op->dimset == BLIS_TEST_NO_DIMS ) + sprintf( &l_str[strlen(l_str)], " %5s", "m" ); + + if( op->dimset == BLIS_TEST_DIMS_MNK || + op->dimset == BLIS_TEST_DIMS_MN || + op->dimset == BLIS_TEST_DIMS_K || + op->dimset == BLIS_TEST_DIMS_MF || + op->dimset == BLIS_TEST_NO_DIMS ) + sprintf( &l_str[strlen(l_str)], " %5s", "n" ); + + if( op->dimset == BLIS_TEST_DIMS_MNK || + op->dimset == BLIS_TEST_DIMS_MK || + op->dimset == BLIS_TEST_DIMS_K ) + sprintf( &l_str[strlen(l_str)], " %5s", "k" ); + + sprintf( &l_str[strlen(l_str)], "%s", " resid result" ); +} + +BlisTestSuite::~BlisTestSuite( ) +{ + free( params.alpha ); + free( params.beta ); + bli_finalize(); + cout << "BlisTestSuite destructor completed" << endl; +} + +bool AoclBlisTestFixture::destroy_params( test_params_t *params ) +{ + unsigned int pci, sci, dci; + char** pc_str = params->pc_str; + char** sc_str = params->sc_str; + char** dc_str = params->dc_str; + + // Free the parameter combination strings and then the master pointer. + for ( pci = 0 ; pci < params->n_param_combos ; ++pci ) + free( pc_str[pci] ); + free( pc_str ); + + // Free the storage combination strings and then the master pointer. + for( sci = 0 ; sci < params->n_store_combos ; ++sci ) + free( sc_str[sci] ); + free( sc_str ); + + // Free the datatype combination strings and then the master pointer. + for( dci = 0 ; dci < params->n_dt_combos ; ++dci ) + free( dc_str[dci] ); + free( dc_str ); + + free( params->dim ); + + return true; +} + +bool AoclBlisTestFixture::libblis_test_preprocess_params + ( + test_params_t* params, + test_op_t* op, + iface_t iface, + const char* p_types, + const char* o_types + ) +{ + unsigned int n_mstorage = params->n_mstorage; + unsigned int n_vstorage = params->n_vstorage; + unsigned int n_datatypes = params->n_datatypes; + + unsigned int mix_all_storage = params->mix_all_storage; + unsigned int mixed_domain = params->mixed_domain; + unsigned int mixed_precision = params->mixed_precision; + char dt_char; + + char* p_spec_str; + unsigned int n_params; + char** chars_for_param; + + unsigned int n_param_combos; + unsigned int n_store_combos; + unsigned int n_dt_combos; + char** pc_str; + char** sc_str; + char** dc_str; + + char s_spec_str[ MAX_NUM_OPERANDS + 1 ]; + unsigned int n_operands; + unsigned int n_operandsp1; + char** chars_for_storage; + + char d_spec_str[ MAX_NUM_OPERANDS + 1 ]; + char** chars_for_spdt; + char** chars_for_dpdt; + unsigned int n_spdt_combos; + unsigned int n_dpdt_combos; + + char** chars_for_dt; + char** chars_for_rddt; + char** chars_for_cddt; + unsigned int n_rddt_combos; + unsigned int n_cddt_combos; + + unsigned int sci, dci, i, j, o; + + // These arrays are malloc()'ed in select branches. Here, we set + // them to NULL so they can be unconditionally free()'ed at the + // end of the function. + chars_for_rddt = NULL; + chars_for_cddt = NULL; + chars_for_spdt = NULL; + chars_for_dpdt = NULL; + + // Set the error-checking level according to what was specified in the + // input file. + if( params->error_checking_level == 0 ) + bli_error_checking_level_set( BLIS_NO_ERROR_CHECKING ); + else + bli_error_checking_level_set( BLIS_FULL_ERROR_CHECKING ); + + // Obtain the parameter specification (filter) string. + p_spec_str = op->params; + + // Figure out how many parameters we have. + n_params = strlen( p_types ); + + if( strlen( p_types ) != strlen( p_spec_str) ) { + libblis_test_printf_error( "Parameter specification string from input file does not match length of p_types string.\n" ); + } + + // Allocate an array that stores pointers to the sets of possible parameter + // chars for each parameter. + chars_for_param = ( char** ) malloc( n_params * sizeof( char* ) ); + + // Set the values in chars_for_param to the appropriate string addresses. + for( i = 0 ; i < n_params ; ++i ) + { + param_t param_type = libblis_test_get_param_type_for_char( p_types[i] ); + chars_for_param[i] = libblis_test_param_chars[ param_type ]; + } + + // Compute the total number of parameter combinations to test (which is + // simply the product of the string lengths of chars_for_param[i]. + n_param_combos = libblis_test_count_combos( n_params, p_spec_str, + chars_for_param ); + + // Allocate an array of parameter combination strings, one for each + // parameter combination that needs to be tested. + pc_str = ( char** ) malloc( n_param_combos * sizeof( char* ) ); + for( i = 0 ; i < n_param_combos ; ++i ) + pc_str[i] = ( char* ) malloc( ( n_params + 1 ) * sizeof( char ) ); + + // Fill the parameter combination strings in pc_str with the parameter + // combinations called for by the parameter string p_spec_str. + libblis_test_fill_param_strings( p_spec_str, + chars_for_param, + n_params, + n_param_combos, + pc_str ); + + // Figure out how many operands we have. + n_operands = strlen( o_types ); + + // If we are testing a micro-kernel, unconditionally disable the + // "mix all storage" option. + if( iface == BLIS_TEST_SEQ_UKERNEL ) + mix_all_storage = DISABLE; + + // Enumerate all combinations of storage schemes requested. + if( mix_all_storage ) + { + // Fill storage specification string with wildcard chars. + for( i = 0 ; i < n_operands ; ++i ) + s_spec_str[i] = '?'; + s_spec_str[i] = '\0'; + + // Allocate an array that stores pointers to the sets of possible + // storage chars for each operand. + chars_for_storage = ( char** ) malloc( n_operands * sizeof( char* ) ); + + // Set the values in chars_for_storage to the address of the string + // that holds the storage chars. + for( i = 0 ; i < n_operands ; ++i ) + { + operand_t operand_type = libblis_test_get_operand_type_for_char( o_types[i] ); + chars_for_storage[i] = libblis_test_store_chars[ operand_type ]; + } + + // Compute the total number of storage combinations to test (which is + // simply the product of the string lengths of chars_for_storage[i]. + n_store_combos = libblis_test_count_combos( n_operands, s_spec_str, + chars_for_storage ); + + // Allocate an array of storage combination strings, one for each + // storage combination that needs to be tested. + sc_str = ( char** ) malloc( n_store_combos * sizeof( char* ) ); + for( sci = 0 ; sci < n_store_combos ; ++sci ) + sc_str[sci] = ( char* ) malloc( ( n_operands + 1 ) * sizeof( char ) ); + + + // Fill the storage combination strings in sc_str with the storage + // combinations called for by the storage string p_spec_str. + libblis_test_fill_param_strings( s_spec_str, + chars_for_storage, + n_operands, + n_store_combos, + sc_str ); + free( chars_for_storage ); + } + else // if ( !mix_all_storage ) + { + // Only run combinations where all operands of either type (matrices + // or vectors) are stored in one storage scheme or another (no mixing + // of schemes within the same operand type). + unsigned int n_mat_operands = 0; + unsigned int n_vec_operands = 0; + + for( o = 0 ; o < n_operands ; ++o ) { + operand_t operand_type + = libblis_test_get_operand_type_for_char( o_types[o] ); + if( operand_type == BLIS_TEST_MATRIX_OPERAND ) + ++n_mat_operands; + else if( operand_type == BLIS_TEST_VECTOR_OPERAND ) + ++n_vec_operands; + } + + // We compute the total number of storage combinations based on whether + // the current operation has only matrix operands, only vector operands, + // or both. + if( n_vec_operands == 0 ) + { + n_store_combos = n_mstorage; + n_vstorage = 1; + } + else if( n_mat_operands == 0 ) + { + n_store_combos = n_vstorage; + n_mstorage = 1; + } + else { + n_store_combos = n_mstorage * n_vstorage; + } + + sc_str = ( char** ) malloc( n_store_combos * sizeof( char* ) ); + + for( j = 0 ; j < n_mstorage ; ++j ) + { + for( i = 0 ; i < n_vstorage ; ++i ) + { + sci = j*n_vstorage + i; + sc_str[ sci ] = ( char* ) malloc( ( n_operands + 1 ) * sizeof( char ) ); + + for( o = 0 ; o < n_operands ; ++o ) + { + unsigned int ij; + operand_t operand_type + = libblis_test_get_operand_type_for_char( o_types[o] ); + + if ( operand_type == BLIS_TEST_MATRIX_OPERAND ) + ij = j; + else + ij = i; + + sc_str[sci][o] = params->storage[ operand_type ][ij]; + } + sc_str[sci][n_operands] = '\0'; + } + } + } + + // Enumerate all combinations of datatype domains requested, but only + // for the gemm operation. + if( !mixed_domain && mixed_precision && op->opid == BLIS_GEMM ) + { + params->is_mixed_dt = TRUE; + + // Increment the number of operands by one to account for the + // computation precision (or computation datatype, as we will encode + // it in the char string). + n_operandsp1 = n_operands + 1; + + unsigned int has_rd = libblis_test_dt_str_has_rd_char( params ); + unsigned int has_cd = libblis_test_dt_str_has_cd_char( params ); + + // Fill datatype specification string with wildcard chars. + for( i = 0 ; i < n_operandsp1 ; ++i ) + d_spec_str[i] = '?'; + d_spec_str[i] = '\0'; + + // Allocate an array that stores pointers to the sets of possible + // datatype chars for each operand. + chars_for_rddt = ( char** ) malloc( n_operandsp1 * sizeof( char* ) ); + chars_for_cddt = ( char** ) malloc( n_operandsp1 * sizeof( char* ) ); + + // Set the values in chars_for_rddt/cddt to the address of the string + // that holds the datatype chars. + for( i = 0 ; i < n_operandsp1 ; ++i ) + { + chars_for_rddt[i] = libblis_test_rd_chars; + chars_for_cddt[i] = libblis_test_cd_chars; + } + + // Set the last set of chars in chars_for_cddt to the real domain + // charset. This is because the last char will be the computation + // precision. + chars_for_cddt[i-1] = libblis_test_rd_chars; + + // Compute the total number of datatype combinations to test (which is + // simply the product of the string lengths of chars_for_spdt/dpdt[i]). + // NOTE: We skip inspecting/branching off of the d_spec_str chars since + // we know they are all '?'. + n_rddt_combos = 0; n_cddt_combos = 0; + + if( has_rd ) + n_rddt_combos = libblis_test_count_combos( n_operandsp1, d_spec_str, + chars_for_rddt ); + + if( has_cd ) + n_cddt_combos = libblis_test_count_combos( n_operandsp1, d_spec_str, + chars_for_cddt ); + + // Add real and complex domain combinations. + n_dt_combos = n_rddt_combos + n_cddt_combos; + + // Allocate an array of datatype combination strings, one for each + // datatype combination that needs to be tested. + dc_str = ( char** ) malloc( n_dt_combos * sizeof( char* ) ); + for( dci = 0 ; dci < n_dt_combos ; ++dci ) + dc_str[dci] = ( char* ) malloc( ( n_operandsp1 + 1 ) * sizeof( char ) ); + + char** dc_str_p = dc_str; + + // Fill the datatype combination strings in dc_str with the datatype + // combinations implied by chars_for_rddt/cddt. + if ( has_rd ) + { + libblis_test_fill_param_strings( d_spec_str, + chars_for_rddt, + n_operandsp1, + n_rddt_combos, + dc_str_p ); + dc_str_p += n_rddt_combos; + } + if ( has_cd ) + { + libblis_test_fill_param_strings( d_spec_str, + chars_for_cddt, + n_operandsp1, + n_cddt_combos, + dc_str_p ); + dc_str_p += n_cddt_combos; + } + } + else if( mixed_domain && !mixed_precision && op->opid == BLIS_GEMM ) + { + params->is_mixed_dt = TRUE; + + // Increment the number of operands by one to account for the + // computation precision (or computation datatype, as we will encode + // it in the char string). + n_operandsp1 = n_operands + 1; + + unsigned int has_sp = libblis_test_dt_str_has_sp_char( params ); + unsigned int has_dp = libblis_test_dt_str_has_dp_char( params ); + + // Fill datatype specification string with wildcard chars. + for( i = 0 ; i < n_operands ; ++i ) + d_spec_str[i] = '?'; + d_spec_str[i] = '\0'; + + // Allocate an array that stores pointers to the sets of possible + // datatype chars for each operand (plus the computation precision + // char). + chars_for_spdt = ( char** ) malloc( n_operands * sizeof( char* ) ); + chars_for_dpdt = ( char** ) malloc( n_operands * sizeof( char* ) ); + + // Set the values in chars_for_spdt/dpdt to the address of the string + // that holds the datatype chars. + for( i = 0 ; i < n_operands ; ++i ) + { + chars_for_spdt[i] = libblis_test_sp_chars; + chars_for_dpdt[i] = libblis_test_dp_chars; + } + + // Compute the total number of datatype combinations to test (which is + // simply the product of the string lengths of chars_for_spdt/dpdt[i]). + // NOTE: We skip inspecting/branching off of the d_spec_str chars since + // we know they are all '?'. + n_spdt_combos = 0; n_dpdt_combos = 0; + + if( has_sp ) + n_spdt_combos = libblis_test_count_combos( n_operands, d_spec_str, + chars_for_spdt ); + + if( has_dp ) + n_dpdt_combos = libblis_test_count_combos( n_operands, d_spec_str, + chars_for_dpdt ); + + // Add single- and double-precision combinations. + n_dt_combos = n_spdt_combos + n_dpdt_combos; + + // Allocate an array of datatype combination strings, one for each + // datatype combination that needs to be tested. + dc_str = ( char** ) malloc( n_dt_combos * sizeof( char* ) ); + for( dci = 0 ; dci < n_dt_combos ; ++dci ) + dc_str[dci] = ( char* ) malloc( ( n_operandsp1 + 1 ) * sizeof( char ) ); + + char** dc_str_p = dc_str; + + // Fill the datatype combination strings in dc_str with the datatype + // combinations implied by chars_for_spdt/dpdt. + if ( has_sp ) + { + libblis_test_fill_param_strings( d_spec_str, + chars_for_spdt, + n_operands, + n_spdt_combos, + dc_str_p ); + dc_str_p += n_spdt_combos; + } + if ( has_dp ) + { + libblis_test_fill_param_strings( d_spec_str, + chars_for_dpdt, + n_operands, + n_dpdt_combos, + dc_str_p ); + dc_str_p += n_dpdt_combos; + } + + // Manually set the computation char to the real projection of the + // first char of each combination. + int prec_i = n_operands; + for( i = 0 ; i < n_dt_combos ; ++i ) + { + dc_str[i][prec_i] = libblis_test_proj_dtchar_to_precchar( dc_str[i][0] ); + dc_str[i][prec_i+1] = '\0'; + } + } + else if( mixed_domain && mixed_precision && op->opid == BLIS_GEMM ) + { + params->is_mixed_dt = TRUE; + + // Increment the number of operands by one to account for the + // computation precision (or computation datatype, as we will encode + // it in the char string). + n_operandsp1 = n_operands + 1; + + // Fill datatype specification string with wildcard chars. + for ( i = 0 ; i < n_operandsp1 ; ++i ) + d_spec_str[i] = '?'; + d_spec_str[i] = '\0'; + + // Allocate an array that stores pointers to the sets of possible + // datatype chars for each operand. + chars_for_dt = ( char** ) malloc( n_operandsp1 * sizeof( char* ) ); + + // Set the values in chars_for_rddt/cddt to the address of the string + // that holds the datatype chars. + for( i = 0; i < n_operandsp1; ++i ) + { + chars_for_dt[i] = libblis_test_dt_chars; + } + + // Set the last set of chars in chars_for_dt to the real domain + // charset. This is because the last char will be the computation + // precision, with the computation domain implied by the operands' + // storage datatypes. + chars_for_dt[i-1] = libblis_test_rd_chars; + + // Compute the total number of datatype combinations to test (which is + // simply the product of the string lengths of chars_for_dt[i]). + // NOTE: We skip inspecting/branching off of the d_spec_str chars since + // we know they are all '?'. + n_dt_combos = libblis_test_count_combos( n_operandsp1, d_spec_str, + chars_for_dt ); + + // Allocate an array of datatype combination strings, one for each + // datatype combination that needs to be tested. + dc_str = ( char** ) malloc( n_dt_combos * sizeof( char* ) ); + for( dci = 0 ; dci < n_dt_combos ; ++dci ) + dc_str[dci] = ( char* ) malloc( ( n_operandsp1 + 1 ) * sizeof( char ) ); + + // Fill the datatype combination strings in dc_str with the datatype + // combinations implied by chars_for_rddt/cddt. + libblis_test_fill_param_strings( d_spec_str, + chars_for_dt, + n_operandsp1, + n_dt_combos, + dc_str ); + } + else // ( ( !mixed_domain && !mixed_precision ) || op->opid != BLIS_GEMM ) + { + params->is_mixed_dt = FALSE; + + // Increment the number of operands by one to account for the + // computation precision (or computation datatype, as we will encode + // it in the char string). + n_operandsp1 = n_operands + 1; + + // Since we are not mixing domains, we only consider n_datatype + // datatype combinations, where each combination is actually + // homogeneous (e.g. "sss", "ddd", etc., if n_operands == 3). + n_dt_combos = n_datatypes; + + // Allocate an array of datatype combination strings, one for each + // datatype specified. + dc_str = ( char** ) malloc( n_dt_combos * sizeof( char* ) ); + for ( dci = 0 ; dci < n_dt_combos ; ++dci ) + dc_str[dci] = ( char* ) malloc( ( n_operandsp1 + 1 ) * sizeof( char ) ); + + // Fill each datatype combination string with the same dt char for + // each operand in the current operation. + for( dci = 0 ; dci < n_dt_combos ; ++dci ) + { + dt_char = params->datatype_char[dci]; + + for ( i = 0; i < n_operands; ++i ) + dc_str[dci][i] = dt_char; + + // Encode the computation precision as the last char. + dc_str[dci][i] = libblis_test_proj_dtchar_to_precchar( dc_str[dci][0] ); + dc_str[dci][i+1] = '\0'; + } + } + + if( (mixed_domain) || (mixed_precision) ) + { + params->is_mixed_dt = TRUE; + } + + { + unsigned int ind, indi; + num_t datatype; + num_t dt_check; + // Loop over the requested datatypes. + for( dci = 0 ; dci < n_dt_combos ; ++dci ) + { + // We need a datatype to use for induced method related things + // as well as to decide which set of residual thresholds to use. + // We must choose the first operand's dt char since that's the + // only operand we know is guaranteed to exist. + bli_param_map_char_to_blis_dt( dc_str[dci][0], &datatype ); + dt_check = datatype; + + int has_sp = + libblis_test_dt_str_has_sp_char_str( n_operandsp1, dc_str[dci] ); + int has_dp = + libblis_test_dt_str_has_dp_char_str( n_operandsp1, dc_str[dci] ); + + int has_samep = ( has_sp && !has_dp ) || ( has_dp && !has_sp ); + + // Notice that we use n_operands here instead of + // n_operandsp1 since we only want to chars for the + // storage datatypes of the matrix operands, not the + // computation precision char. + int has_cd_only = + !libblis_test_dt_str_has_rd_char_str( n_operands, dc_str[dci] ); + + if( has_sp ) + { + // If any of the operands are single precision, ensure that + // dt_check is also single precision so that the residual is + // compared to datatype-appropriate thresholds. + dt_check = bli_dt_proj_to_single_prec( datatype ); + } + + // Start by assuming we will only test native execution. + ind_t ind_first = BLIS_NAT; + dim_t ind_last = BLIS_NAT; + + // If the operation is level-3, and all operand domains are complex, + // then we iterate over all induced methods. + if( bli_opid_is_level3( op->opid ) && has_cd_only ) + ind_first = BLIS_IND_FIRST; + + // Loop over induced methods (or just BLIS_NAT). + ind = 0; + for( indi = ind_first ; indi <= ind_last ; ++indi ) + { + // If the current datatype is real, OR if the current + // induced method is implemented (for the operation + // being tested) AND it was requested, then we enable + // ONLY that method and proceed. Otherwise, we skip the + // current method and go to the next method. + if( bli_is_real( datatype ) ) { ; } + else if( bli_ind_oper_is_impl( op->opid, (ind_t)indi ) && + params->ind_enable[ indi ] == 1 ) + { + // If the current induced method is 1m, make sure that + // we only proceed for gemm where all operands are stored + // in the complex domain. (This prevents 1m from being + // executed on mixed-datatype combinations that contain + // real domain datatypes.) + if ( indi == BLIS_1M ) + { + if ( op->opid == BLIS_GEMM && has_cd_only ) { ; } + else if ( has_samep && has_cd_only ) { ; } + else { continue; } + } + else { ; } + } + else { continue; } + params->indim[dci][ind] = indi; + ind++; + } + params->indn[dci] = ind; + params->dt[dci] = dt_check; + } + } + // Free the array that stored pointers to the sets of possible parameter + // chars for each parameter. + free( chars_for_param ); + + // Free some auxiliary arrays used by the mixed-domain/mixed-precision + // datatype-handling logic. + free( chars_for_rddt ); + free( chars_for_cddt ); + free( chars_for_spdt ); + free( chars_for_dpdt ); + /* + if ( params->bitextf ) { + params->dimf = 0; + } + */ + + if ( params->nanf ) + { + params->bitextf = 0; + } + + params->n_param_combos = n_param_combos; + params->n_store_combos = n_store_combos; + params->n_dt_combos = n_dt_combos; + + params->pc_str = pc_str; + params->sc_str = sc_str; + params->dc_str = dc_str; + + if (params->abf != 1) + params->nab = 1; + + if(params->ldf == 1) + { + params->ld[0] = (int)rand()%100; + params->ld[1] = (int)rand()%100; + params->ld[2] = (int)rand()%100; + } + else + { + params->ld[0] = 0; + params->ld[1] = 0; + params->ld[2] = 0; + } + + tensor_t *v; + unsigned int d = ( ( ( params->p_max - params->p_first ) + params->p_inc ) / params->p_inc ); + j = 0; + if( params->dimf == 1 ) + { + if( op->n_dims == 3) + { + unsigned int nc = 1; + for( i = 0 ; i < op->n_dims ; i++ ) + { + if( op->dim_spec[i] < 0 ) + nc *= d; + } + params->ndim = nc; + v = ( tensor_t* ) malloc( nc * sizeof( tensor_t ) ); + dim_t mm = abs( op->dim_spec[0] ); + dim_t nn = abs( op->dim_spec[1] ); + dim_t kk = abs( op->dim_spec[2] ); + dim_t m,n,k; + dim_t mmax = ( op->dim_spec[0] > 0 ) ? params->p_first : params->p_max ; + dim_t nmax = ( op->dim_spec[1] > 0 ) ? params->p_first : params->p_max ; + dim_t kmax = ( op->dim_spec[2] > 0 ) ? params->p_first : params->p_max ; + for( k = params->p_first ; k <= kmax ; k += params->p_inc ) + { + for( n = params->p_first ; n <= nmax ; n += params->p_inc ) + { + for( m = params->p_first ; m <= mmax ; m += params->p_inc ) + { + v[j].m = ( op->dim_spec[0] > 0 ) ? op->dim_spec[0] : (m/mm) ; + v[j].n = ( op->dim_spec[1] > 0 ) ? op->dim_spec[1] : (n/nn) ; + v[j].k = ( op->dim_spec[2] > 0 ) ? op->dim_spec[2] : (k/kk) ; + j++; + } + } + } + params->ndim = j; + } + else if(op->n_dims == 2) + { + unsigned int nc = 1; + for( i = 0 ; i < op->n_dims ; i++ ) + { + if( op->dim_spec[i] < 0 ) + nc *= d; + } + params->ndim = nc; + v = ( tensor_t* ) malloc( nc * sizeof( tensor_t ) ); + dim_t mm = abs( op->dim_spec[0] ); + dim_t nn = abs( op->dim_spec[1] ); + dim_t m,n; + dim_t mmax = ( op->dim_spec[0] > 0 ) ? params->p_first : params->p_max ; + dim_t nmax = ( op->dim_spec[1] > 0 ) ? params->p_first : params->p_max ; + for( n = params->p_first ; n <= nmax ; n += params->p_inc ) + { + for( m = params->p_first ; m <= mmax ; m += params->p_inc ) + { + v[j].m = ( op->dim_spec[0] > 0 ) ? op->dim_spec[0] : (m/mm) ; + v[j].n = ( op->dim_spec[1] > 0 ) ? op->dim_spec[1] : (n/nn) ; + v[j].k = 0; + j++; + } + } + params->ndim = j; + } + else + { + unsigned int nc = 1; + for( i = 0 ; i < op->n_dims ; i++ ) + { + if( op->dim_spec[i] < 0 ) + nc *= d; + } + params->ndim = nc; + v = ( tensor_t* ) malloc( nc * sizeof( tensor_t ) ); + dim_t mm = abs( op->dim_spec[0] ); + dim_t m; + dim_t mmax = ( op->dim_spec[0] > 0 ) ? params->p_first : params->p_max ; + for( m = params->p_first ; m <= mmax; m += params->p_inc ) { + v[j].m = ( op->dim_spec[0] > 0 ) ? op->dim_spec[0] : (m/mm) ; + v[j].n = 0; + v[j].k = 0; + j++; + } + params->ndim = j; + } + } + else + { + if( op->n_dims == 3 ) + { + if( SIGN( op->dim_spec[0] ) && + SIGN( op->dim_spec[1] ) && + SIGN( op->dim_spec[2] ) ) + { + d = 1 ; + } + params->ndim = d; + v = ( tensor_t* ) malloc( d * sizeof( tensor_t ) ); + dim_t mm = abs( op->dim_spec[0] ); + dim_t nn = abs( op->dim_spec[1] ); + dim_t kk = abs( op->dim_spec[2] ); + dim_t m; + dim_t mmax = d == 1 ? params->p_first : params->p_max ; + for( m = params->p_first ; m <= mmax ; m += params->p_inc ) + { + v[j].m = ( op->dim_spec[0] > 0 ) ? op->dim_spec[0] : (m/mm) ; + v[j].n = ( op->dim_spec[1] > 0 ) ? op->dim_spec[1] : (m/nn) ; + v[j].k = ( op->dim_spec[2] > 0 ) ? op->dim_spec[2] : (m/kk) ; + j++; + } + params->ndim = j; + } + else if( op->n_dims == 2 ) + { + if( SIGN( op->dim_spec[0] ) && SIGN( op->dim_spec[1] ) ) + { + d = 1 ; + } + params->ndim = d; + v = ( tensor_t* ) malloc( d * sizeof( tensor_t ) ); + dim_t mm = abs( op->dim_spec[0] ); + dim_t nn = abs( op->dim_spec[1] ); + dim_t m; + dim_t mmax = d == 1 ? params->p_first : params->p_max ; + for( m = params->p_first ; m <= mmax; m += params->p_inc ) + { + v[j].m = ( op->dim_spec[0] > 0 ) ? op->dim_spec[0] : (m/mm) ; + v[j].n = ( op->dim_spec[1] > 0 ) ? op->dim_spec[1] : (m/nn) ; + v[j].k = 0; + j++; + } + params->ndim = j; + } + else + { + if( SIGN( op->dim_spec[0] ) ) + { + d = 1 ; + } + params->ndim = d; + v = ( tensor_t* ) malloc( d * sizeof( tensor_t ) ); + dim_t mm = abs(op->dim_spec[0]); + dim_t m; + dim_t mmax = d == 1 ? params->p_first : params->p_max ; + for ( m = params->p_first ; m <= mmax; m += params->p_inc) + { + v[j].m = ( op->dim_spec[0] > 0 ) ? op->dim_spec[0] : (m/mm) ; + v[j].n = 0 ; + v[j].k = 0; + j++; + } + params->ndim = j; + } + } + params->dim = v; + return true; +} + +static int test_check_func( test_op_t* op ) +{ + return op->op_switch; +} + +void BlisTestSuite::CreateGtestFilters( test_ops_t* ops, string &str ) +{ + if( test_check_func( &(ops->randv) ) ) + { + str = str + "*AOCL_BLIS_RANDV"; + str = str + "*:"; + } + if( test_check_func( &(ops->randm) ) ) + { + str = str + "*AOCL_BLIS_RANDM"; + str = str + "*:"; + } + if( test_check_func( &(ops->addv) ) ) + { + str = str + "*AOCL_BLIS_ADDV"; + str = str + "*:"; + } + if( test_check_func( &(ops->amaxv) ) ) + { + str = str + "*AOCL_BLIS_AMAXV"; + str = str + "*:"; + } + if( test_check_func( &(ops->axpbyv) ) ) + { + str = str + "*AOCL_BLIS_AXPBYV"; + str = str + "*:"; + } + if( test_check_func( &(ops->axpyv) ) ) + { + str = str + "*AOCL_BLIS_AXPYV"; + str = str + "*:"; + } + if( test_check_func( &(ops->copyv) ) ) + { + str = str + "*AOCL_BLIS_COPYV"; + str = str + "*:"; + } + if( test_check_func( &(ops->dotv) ) ) + { + str = str + "*AOCL_BLIS_DOTV"; + str = str + "*:"; + } + if( test_check_func( &(ops->dotxv) ) ) + { + str = str + "*AOCL_BLIS_DOTXV"; + str = str + "*:"; + } + if( test_check_func( &(ops->normfv) ) ) + { + str = str + "*AOCL_BLIS_NORMFV"; + str = str + "*:"; + } + if( test_check_func( &(ops->scal2v) ) ) + { + str = str + "*AOCL_BLIS_SCAL2V"; + str = str + "*:"; + } + if( test_check_func( &(ops->scalv) ) ) + { + str = str + "*AOCL_BLIS_SCALV"; + str = str + "*:"; + } + if( test_check_func( &(ops->setv) ) ) + { + str = str + "*AOCL_BLIS_SETV"; + str = str + "*:"; + } + if( test_check_func( &(ops->xpbyv) ) ) + { + str = str + "*AOCL_BLIS_XPBYV"; + str = str + "*:"; + } + if( test_check_func( &(ops->subv) ) ) + { + str = str + "*AOCL_BLIS_SUBV"; + str = str + "*:"; + } + if( test_check_func( &(ops->axpyf) ) ) + { + str = str + "*AOCL_BLIS_AXPYF"; + str = str + "*:"; + } + if( test_check_func( &(ops->axpy2v) ) ) + { + str = str + "*AOCL_BLIS_AXPY2V"; + str = str + "*:"; + } + if( test_check_func( &(ops->dotxf) ) ) + { + str = str + "*AOCL_BLIS_DOTXF"; + str = str + "*:"; + } + if( test_check_func( &(ops->dotaxpyv) ) ) + { + str = str + "*AOCL_BLIS_DOTAXPYV"; + str = str + "*:"; + } + if( test_check_func( &(ops->dotxaxpyf) ) ) + { + str = str + "*AOCL_BLIS_DOTXAXPYF"; + str = str + "*:"; + } + if( test_check_func( &(ops->addm) ) ) + { + str = str + "*AOCL_BLIS_ADDM"; + str = str + "*:"; + } + if( test_check_func( &(ops->axpym) ) ) + { + str = str + "*AOCL_BLIS_AXPYM"; + str = str + "*:"; + } + if( test_check_func( &(ops->copym) ) ) + { + str = str + "*AOCL_BLIS_COPYM"; + str = str + "*:"; + } + if( test_check_func( &(ops->normfm) ) ) + { + str = str + "*AOCL_BLIS_NORMFM"; + str = str + "*:"; + } + if( test_check_func( &(ops->scalm) ) ) + { + str = str + "*AOCL_BLIS_SCALM"; + str = str + "*:"; + } + if( test_check_func( &(ops->scal2m) ) ) + { + str = str + "*AOCL_BLIS_SCAL2M"; + str = str + "*:"; + } + if( test_check_func( &(ops->setm) ) ) + { + str = str + "*AOCL_BLIS_SETM"; + str = str + "*:"; + } + if( test_check_func( &(ops->subm) ) ) + { + str = str + "*AOCL_BLIS_SUBM"; + str = str + "*:"; + } + if( test_check_func( &(ops->xpbym) ) ) + { + str = str + "*AOCL_BLIS_XPBYM"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemv) ) ) + { + str = str + "*AOCL_BLIS_GEMV"; + str = str + "*:"; + } + if( test_check_func( &(ops->ger) ) ) + { + str = str + "*AOCL_BLIS_GER"; + str = str + "*:"; + } + if( test_check_func( &(ops->hemv) ) ) + { + str = str + "*AOCL_BLIS_HEMV"; + str = str + "*:"; + } + if( test_check_func( &(ops->her) ) ) + { + str = str + "*AOCL_BLIS_L2HER"; + str = str + "*:"; + } + if( test_check_func( &(ops->her2) ) ) + { + str = str + "*AOCL_BLIS_L2HER2"; + str = str + "*:"; + } + if( test_check_func( &(ops->symv) ) ) + { + str = str + "*AOCL_BLIS_SYMV"; + str = str + "*:"; + } + if( test_check_func( &(ops->syr) ) ) + { + str = str + "*AOCL_BLIS_L2SYR"; + str = str + "*:"; + } + if( test_check_func( &(ops->syr2) ) ) + { + str = str + "*AOCL_BLIS_L2SYR2"; + str = str + "*:"; + } + if( test_check_func( &(ops->trmv) ) ) + { + str = str + "*AOCL_BLIS_TRMV"; + str = str + "*:"; + } + if( test_check_func( &(ops->trsv) ) ) + { + str = str + "*AOCL_BLIS_TRSV"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm) ) ) + { + str = str + "*AOCL_BLIS_GEMM"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemmt) ) ) + { + str = str + "*AOCL_BLIS_L3GEMMT"; + str = str + "*:"; + } + if( test_check_func( &(ops->hemm) ) ) + { + str = str + "*AOCL_BLIS_HEMM"; + str = str + "*:"; + } + if( test_check_func( &(ops->herk) ) ) + { + str = str + "*AOCL_BLIS_HERK"; + str = str + "*:"; + } + if( test_check_func( &(ops->her2k) ) ) + { + str = str + "*AOCL_BLIS_HER2K"; + str = str + "*:"; + } + if( test_check_func( &(ops->symm) ) ) + { + str = str + "*AOCL_BLIS_SYMM"; + str = str + "*:"; + } + if( test_check_func( &(ops->syrk) ) ) + { + str = str + "*AOCL_BLIS_SYRK"; + str = str + "*:"; + } + if( test_check_func( &(ops->syr2k) ) ) + { + str = str + "*AOCL_BLIS_SYR2K"; + str = str + "*:"; + } + if( test_check_func( &(ops->trmm) ) ) + { + str = str + "*AOCL_BLIS_L3TRMM"; + str = str + "*:"; + } + if( test_check_func( &(ops->trmm3) ) ) + { + str = str + "*AOCL_BLIS_TRMM3"; + str = str + "*:"; + } + if( test_check_func( &(ops->trsm) ) ) + { + str = str + "*AOCL_BLIS_TRSM"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_u8s8s32os32) ) ) + { + str = str + "*AOCL_GEMM_S32S32"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_u8s8s32os8) ) ) + { + str = str + "*AOCL_GEMM_S8S32"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_f32f32f32of32) ) ) + { + str = str + "*AOCL_GEMM_F32F32"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_u8s8s16os16) ) ) + { + str = str + "*AOCL_GEMM_S16S16"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_u8s8s16os8) ) ) + { + str = str + "*AOCL_GEMM_S8S16"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_bf16bf16f32of32) ) ) + { + str = str + "*AOCL_GEMM_F32BF16"; + str = str + "*:"; + } + if( test_check_func( &(ops->gemm_bf16bf16f32obf16) ) ) + { + str = str + "*AOCL_GEMM_BF16BF16"; + str = str + "*:"; + } + cout << "Filter_data :" << str.c_str() << endl; +} + +int BlisTestSuite::libblis_test_inpfile( char* filename, input_file_t* pfile ) +{ + ifstream input_filename( filename ); + if( !input_filename.is_open() ) + { + cerr << "Could not open the input file - '" << filename << " " << endl; + return EXIT_FAILURE; + } + strncpy( pfile->inputfile, filename, MAX_FILENAME_LENGTH ); + pfile->fileread = 1; + + input_filename.close(); + return 0; +} + +int AoclBlisTestFixture::libblis_test_read_params_inpfile( char* filename, + test_params_t* params, test_ops_t* ops, printres_t* pfr ) +{ + string line; + ifstream input_filename( filename ); + + if( !input_filename.is_open() ) + { + cerr << "Could not open the input file - '" << filename << " " << endl; + return EXIT_FAILURE; + } + + while( getline(input_filename, line) ) + { + libblis_read_inpprms( line, params, ops, pfr ); + } + + input_filename.close(); + return 0; +} + +bool AoclBlisTestFixture::create_params( test_params_t *params ) +{ + char** pc_str; + char** sc_str; + char** dc_str; + unsigned int i; + unsigned int n_params = 4; //max n_params = 4 + unsigned int n_dims = 3; //max n_dims = 3 + + params->n_param_combos = n_params; + params->n_store_combos = n_dims; + params->n_dt_combos = 1; + + // Free the parameter combination strings and then the master pointer. + pc_str = ( char** ) malloc( params->n_param_combos * sizeof( char* ) ); + for ( i = 0 ; i < params->n_param_combos ; ++i ) + { + pc_str[i] = ( char* ) malloc( ( n_params + 1 ) * sizeof( char ) ); + memset( pc_str[i], 0, ( n_params + 1 ) ); + } + + // Free the storage combination strings and then the master pointer. + sc_str = ( char** ) malloc( params->n_store_combos * sizeof( char* ) ); + for ( i = 0 ; i < params->n_store_combos ; ++i ) + { + sc_str[i] = ( char* ) malloc( ( n_dims + 1 ) * sizeof( char ) ); + memset( sc_str[i], 0, ( n_dims + 1 ) ); + } + + // Free the datatype combination strings and then the master pointer. + dc_str = ( char** ) malloc( params->n_dt_combos * sizeof( char* ) ); + for ( i = 0 ; i < params->n_dt_combos ; ++i ) + { + dc_str[i] = ( char* ) malloc( ( params->n_dt_combos + 1 ) * sizeof( char ) ); + memset( dc_str[i], 0, ( params->n_dt_combos + 1 ) ); + } + + params->n_repeats = 1 ; + + params->rand_method = 0 ; + params->gs_spacing = 32; + params->alignment = 0 ; + params->n_app_threads = 1 ; + params->error_checking_level = 1; + + params->bitextf = 0 ; + params->nab = 1 ; + params->passflag = 1 ; + params->api = API_CBLAS; + + params->pc_str = pc_str; + params->sc_str = sc_str; + params->dc_str = dc_str; + + params->alpha = ( atom_t * ) malloc( params->nab * sizeof( atom_t ) ); + params->beta = ( atom_t * ) malloc( params->nab * sizeof( atom_t ) ); + + params->dim = ( tensor_t* ) malloc(3 * sizeof( tensor_t ) ); + memset( params->dim, 0, (3 * sizeof( tensor_t )) ); + + return true; +} + +void BlisTestSuite::CreateGtestFilters_api(input_file_t* pfile, string &str) +{ + if(pfile->fileread == 1) + { + str = str + "*AOCL_BLIS_READ_INPUTFILE"; + str = str + "*:"; + } + cout << "Filter_data :" << str.c_str() << endl; +} diff --git a/gtestsuite/src/test_randm.cpp b/gtestsuite/src/test_randm.cpp new file mode 100644 index 000000000..002d8aff0 --- /dev/null +++ b/gtestsuite/src/test_randm.cpp @@ -0,0 +1,227 @@ +#include "blis_test.h" +#include "blis_utils.h" + +void libblis_test_randm_impl(iface_t iface, obj_t* x ); + +double libblis_test_randm_check(test_params_t* params, obj_t* x ); + +double libblis_test_bitrp_randm( + test_params_t* params, + iface_t iface, + obj_t* x, + num_t dt +) { + double resid = 0.0; + resid = libblis_test_matrix_check(params, x); + return resid; +} + +double libblis_test_op_randm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* sc_str, + tensor_t* dim +) +{ + num_t datatype; + dim_t m, n; + char x_store; + double resid = 0.0; + obj_t x; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Extract the storage character for each operand. + x_store = sc_str[0]; + + // Create the test objects. + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, x_store, m, n, &x ); + + libblis_test_randm_impl( iface, &x ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + resid = libblis_test_bitrp_randm( params, iface, &x, datatype); + } + else { + resid = libblis_test_randm_check( params, &x ); + } +#endif + + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_randm_impl ( + iface_t iface, + obj_t* x +) +{ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_randm( x ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +void absummval(dim_t m, dim_t n, float* x, inc_t rs_x, inc_t cs_x, float* sum_x) +{ + float abs_chi1; + float sum; + dim_t i, j; + + bli_sset0s(sum); + for ( j = 0; j < n; j++ ) { + for ( i = 0; i < m; i++ ) { + float* chi1 = x + i*rs_x + j*cs_x; + bli_ssabval2s(*chi1, abs_chi1); + bli_ssadds(abs_chi1, sum); + } + } + + bli_sscopys(sum, *sum_x); +} + +void absummval(dim_t m, dim_t n, double* x, inc_t rs_x, inc_t cs_x, double* sum_x) +{ + double abs_chi1; + double sum; + dim_t i, j; + + bli_dset0s(sum); + for ( j = 0; j < n; j++ ) { + for ( i = 0; i < m; i++ ) { + double* chi1 = x + i*rs_x + j*cs_x; + bli_ddabval2s(*chi1, abs_chi1); + bli_ddadds(abs_chi1, sum); + } + } + + bli_ddcopys(sum, *sum_x); +} + +void absummval(dim_t m, dim_t n, scomplex* x, inc_t rs_x, inc_t cs_x, float* sum_x) +{ + float abs_chi1; + float sum; + dim_t i, j; + + bli_sset0s(sum); + for ( j = 0; j < n; j++ ) { + for ( i = 0; i < m; i++ ) { + scomplex* chi1 = x + i*rs_x + j*cs_x; + bli_csabval2s(*chi1, abs_chi1); + bli_ssadds(abs_chi1, sum); + } + } + + bli_sscopys(sum, *sum_x); +} + +void absummval(dim_t m, dim_t n, dcomplex* x, inc_t rs_x, inc_t cs_x, double* sum_x) +{ + double abs_chi1; + double sum; + dim_t i, j; + + bli_dset0s(sum); + for ( j = 0; j < n; j++ ) { + for ( i = 0; i < m; i++ ) { + dcomplex* chi1 = x + i*rs_x + j*cs_x; + bli_zdabval2s(*chi1, abs_chi1); + bli_ddadds(abs_chi1, sum); + } + } + + bli_ddcopys(sum, *sum_x); +} + +template +void absumm ( obj_t* x, obj_t* sum_x ) +{ + dim_t m = bli_obj_length( x ); + dim_t n = bli_obj_width( x ); + + T* buf_x =(T*) bli_obj_buffer_at_off( x ); + inc_t rs_x = bli_obj_row_stride( x ); + inc_t cs_x = bli_obj_col_stride( x ); + + U* buf_sum_x = (U*)bli_obj_buffer_at_off( sum_x ); + + // Invoke the function. + absummval( m, n, buf_x, rs_x, cs_x, buf_sum_x ); +} + +double libblis_test_randm_check( test_params_t* params, obj_t* x ) +{ + num_t dt = bli_obj_dt( x ); + num_t dt_real = bli_obj_dt_proj_to_real( x ); + dim_t m_x = bli_obj_length( x ); + dim_t n_x = bli_obj_width( x ); + obj_t sum; + + // + // The two most likely ways that randm would fail is if all elements + // were zero, or if all elements were greater than or equal to one. + // We check both of these conditions by computing the sum of the + // absolute values of the elements of x. + // + + double resid = 0.0; + + bli_obj_scalar_init_detached( dt_real, &sum ); + + switch( dt ) { + case BLIS_FLOAT : + { + absumm( x, &sum ); + break; + } + case BLIS_DOUBLE : + { + absumm( x, &sum ); + break; + } + case BLIS_SCOMPLEX : + { + absumm( x, &sum ); + break; + } + case BLIS_DCOMPLEX : + { + absumm( x, &sum ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + if ( bli_is_float( dt_real )) { + float* sum_x = (float*)bli_obj_buffer_at_off( &sum ); + + if ( *sum_x == *bli_d0 ) resid = 1.0; + else if ( *sum_x >= 2.0 * m_x * n_x ) resid = 2.0; + } + else /* if ( bli_is_double( dt_real ) )*/ { + double* sum_x = (double*)bli_obj_buffer_at_off( &sum ); + + if ( *sum_x == *bli_d0 ) resid = 1.0; + else if ( *sum_x >= 2.0 * m_x * n_x ) resid = 2.0; + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_randv.cpp b/gtestsuite/src/test_randv.cpp new file mode 100644 index 000000000..d627f645b --- /dev/null +++ b/gtestsuite/src/test_randv.cpp @@ -0,0 +1,107 @@ +#include "blis_test.h" +#include "blis_utils.h" + +void libblis_test_randv_impl(iface_t iface, obj_t* x ); + +double libblis_test_randv_check(test_params_t* params, obj_t* x ); + +double libblis_test_bitrp_randv( + test_params_t* params, + iface_t iface, + obj_t* x, + num_t dt +) +{ + double resid = 0.0; + resid = libblis_test_vector_check(params, x); + return resid; +} + +double libblis_test_op_randv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* sc_str, + tensor_t* dim +) +{ + num_t datatype; + dim_t m; + char x_store; + obj_t x; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Extract the storage character for each operand. + x_store = sc_str[0]; + + // Create the test objects. + libblis_test_vobj_create( params, datatype, x_store, m, &x ); + + libblis_test_randv_impl( iface, &x ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + resid = libblis_test_bitrp_randv( params, iface, &x, datatype); + } + else { + resid = libblis_test_randv_check( params, &x ); + } +#endif + + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_randv_impl( + iface_t iface, + obj_t* x +) +{ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_randv( x ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_randv_check (test_params_t* params, obj_t* x ) +{ + num_t dt_real = bli_obj_dt_proj_to_real( x ); + dim_t m_x = bli_obj_vector_dim( x ); + obj_t sum; + + double resid = 0.0; + + bli_obj_scalar_init_detached( dt_real, &sum ); + + bli_norm1v( x, &sum ); + + if (bli_is_float( dt_real )) { + float* sum_x = (float*)bli_obj_buffer_at_off( &sum ); + + if ( *sum_x == *bli_d0 ) resid = 1.0; + else if ( *sum_x >= 2.0 * m_x ) resid = 2.0; + } + else /* if ( bli_is_double(dt_real )) */ { + double* sum_x = (double*)bli_obj_buffer_at_off( &sum ); + + if ( *sum_x == *bli_d0 ) resid = 1.0; + else if ( *sum_x >= 2.0 * m_x ) resid = 2.0; + } + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_scal2m.cpp b/gtestsuite/src/test_scal2m.cpp new file mode 100644 index 000000000..66e891688 --- /dev/null +++ b/gtestsuite/src/test_scal2m.cpp @@ -0,0 +1,226 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scal2m.h" + +// Local prototypes. +void libblis_test_scal2m_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_scal2m_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +); + +double libblis_test_scal2m_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_save +); + +double libblis_ref_scal2m( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_scal2m_check( params, alpha, x, y, y_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iscal2m_check( params, alpha, x, y, y_orig); + } + else { + resid = libblis_test_matrix_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_scal2m( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( y_orig, r ); + libblis_test_scal2m_impl( iface, alpha, x, r ); + resid = libblis_test_bitrp_matrix(y, r, dt); + } + return resid; +} + + +double libblis_test_op_scal2m( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m, n; + trans_t transx; + obj_t alpha, x, y; + obj_t y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_trans( pc_str[0], &transx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transx, + sc_str[0], m, n, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y_save ); + + // Set alpha. + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &alpha ); + else + bli_setsc( 0.0, -2.0, &alpha ); + + // Randomize and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_mobj_randomize( params, FALSE, &x ); + } else { + libblis_test_mobj_irandomize( params, &x ); + } + bli_setm( &BLIS_ONE, &y ); + bli_copym( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conjtrans( transx, &x ); + + libblis_test_scal2m_impl( iface, &alpha, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + + resid = libblis_test_bitrp_scal2m( params, iface, &alpha, &x, + &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_scal2m( params, &alpha, &x, &y, &y_save); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_scal2m_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_scal2m( alpha, x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_scal2m_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_length( y ); + dim_t n = bli_obj_width( y ); + + obj_t x_temp; + obj_t norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // - y_orig is set to one. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := alpha * conjx(x) + // + // is functioning correctly if + // + // normfm( y - alpha * conjx(x) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, n, 0, 0, &x_temp ); + + bli_copym( x, &x_temp ); + + bli_scalm( alpha, &x_temp ); + + bli_subm( &x_temp, y ); + bli_normfm( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_scal2m.h b/gtestsuite/src/test_scal2m.h new file mode 100644 index 000000000..62aa0758e --- /dev/null +++ b/gtestsuite/src/test_scal2m.h @@ -0,0 +1,17 @@ +#ifndef TEST_SCAL2M_H +#define TEST_SCAL2M_H + +#include "blis_test.h" + +double libblis_test_iscal2m_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_scal2m( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SCAL2M_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_scal2v.cpp b/gtestsuite/src/test_scal2v.cpp new file mode 100644 index 000000000..9189438a4 --- /dev/null +++ b/gtestsuite/src/test_scal2v.cpp @@ -0,0 +1,222 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scal2v.h" + +// Local prototypes. +void libblis_test_scal2v_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_scal2v_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +); + +double libblis_test_scal2v_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +); + +double libblis_ref_scal2v( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_scal2v_check( params, alpha, x, y, y_save); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iscal2v_check( params, alpha, x, y, y_save); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_scal2v( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_scal2v_impl( iface, alpha, x, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_scal2v ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t alpha, x, y; + obj_t y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + // Set alpha. + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &alpha ); + else + bli_setsc( 0.0, -2.0, &alpha ); + + // Randomize x and y, and save y. + // Randomize and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + } else { + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + libblis_test_scal2v_impl( iface, &alpha, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_scal2v( params, iface, &alpha, &x, + &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_scal2v( params, &alpha, &x, &y, &y_save); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_scal2v_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_scal2v( alpha, x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_scal2v_check ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t x_temp; + obj_t norm; + + double junk; + double resid = 0.0 ; + // + // Pre-conditions: + // - x is randomized. + // - y_orig is set to one. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := alpha * conjx(x) + // + // is functioning correctly if + // + // normfv( y - alpha * conjx(x) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &x_temp ); + + bli_copyv( x, &x_temp ); + + bli_scalv( alpha, &x_temp ); + + bli_subv( &x_temp, y ); + bli_normfv( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_scal2v.h b/gtestsuite/src/test_scal2v.h new file mode 100644 index 000000000..2edca5359 --- /dev/null +++ b/gtestsuite/src/test_scal2v.h @@ -0,0 +1,17 @@ +#ifndef TEST_SCAL2V_H +#define TEST_SCAL2V_H + +#include "blis_test.h" + +double libblis_test_iscal2v_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_scal2v( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SCAL2V_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_scalm.cpp b/gtestsuite/src/test_scalm.cpp new file mode 100644 index 000000000..dbf9aa648 --- /dev/null +++ b/gtestsuite/src/test_scalm.cpp @@ -0,0 +1,221 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scalm.h" + +// Local prototypes. +void libblis_test_scalm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_scalm_impl ( + iface_t iface, + obj_t* beta, + obj_t* y +); + +double libblis_test_scalm_check ( + test_params_t* params, + obj_t* beta, + obj_t* y, + obj_t* y_save +); + +double libblis_ref_scalm( + test_params_t* params, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_scalm_check( params, beta, y, y_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iscalm_check( params, beta, y, y_orig); + } + else { + resid = libblis_test_matrix_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_scalm( + test_params_t* params, + iface_t iface, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( y_orig, r ); + libblis_test_scalm_impl( iface, beta, r ); + resid = libblis_test_bitrp_matrix(y, r, dt); + } + return resid; +} + +double libblis_test_op_scalm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m, n; + conj_t conjbeta; + obj_t beta, y; + obj_t y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjbeta ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y_save ); + + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_mobj_randomize( params, FALSE, &y ); + } else { + libblis_test_mobj_irandomize( params, &y ); + } + + bli_copym( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjbeta, &beta ); + + libblis_test_scalm_impl( iface, &beta, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + + resid = libblis_test_bitrp_scalm( params, iface, &beta, &y, + &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_scalm( params, &beta, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_scalm_impl( + iface_t iface, + obj_t* beta, + obj_t* y +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_scalm( beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_scalm_check( + test_params_t* params, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_length( y ); + dim_t n = bli_obj_width( y ); + + obj_t norm_y_r; + obj_t nbeta; + + obj_t y2; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - y_orig is randomized. + // Note: + // - beta should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := conjbeta(beta) * y_orig + // + // is functioning correctly if + // + // normfm( y + -conjbeta(beta) * y_orig ) + // + // is negligible. + // + + bli_obj_create( dt, m, n, 0, 0, &y2 ); + bli_copym( y_orig, &y2 ); + + bli_obj_scalar_init_detached( dt, &nbeta ); + bli_obj_scalar_init_detached( dt_real, &norm_y_r ); + + bli_copysc( beta, &nbeta ); + bli_mulsc( &BLIS_MINUS_ONE, &nbeta ); + + bli_scalm( &nbeta, &y2 ); + bli_addm( &y2, y ); + + bli_normfm( y, &norm_y_r ); + + bli_getsc( &norm_y_r, &resid, &junk ); + + bli_obj_free( &y2 ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_scalm.h b/gtestsuite/src/test_scalm.h new file mode 100644 index 000000000..abbb92e4a --- /dev/null +++ b/gtestsuite/src/test_scalm.h @@ -0,0 +1,16 @@ +#ifndef TEST_SCALM_H +#define TEST_SCALM_H + +#include "blis_test.h" + +double libblis_test_iscalm_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_scalm( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SCALM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_scalv.cpp b/gtestsuite/src/test_scalv.cpp new file mode 100644 index 000000000..5ca85e290 --- /dev/null +++ b/gtestsuite/src/test_scalv.cpp @@ -0,0 +1,505 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_scalv.h" + +// Local prototypes. +void libblis_test_scalv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_scalv_impl ( + iface_t iface, + obj_t* beta, + obj_t* y +); + +double libblis_test_scalv_check ( + test_params_t* params, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +double cblas_scalv( + f77_int mm, + obj_t* beta, + obj_t* x, + f77_int incx, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* betap = (float*) bli_obj_buffer( beta ); + float* xp = (float*) bli_obj_buffer( x ); + cblas_sscal( mm, *betap, xp, incx ); + break; + } + case BLIS_DOUBLE : + { + double* betap = (double*) bli_obj_buffer( beta ); + double* xp = (double*) bli_obj_buffer( x ); + cblas_dscal( mm, *betap, xp, incx ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cblas_cscal( mm, betap, xp, incx ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + cblas_zscal( mm, betap, xp, incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double cblas_cscalv( + f77_int mm, + obj_t* beta, + obj_t* x, + f77_int incx, + num_t dt +){ + switch( dt ) { + case BLIS_SCOMPLEX : + { + float* betap = (float*) bli_obj_buffer( beta ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cblas_csscal( mm, *betap, xp, incx ); + break; + } + case BLIS_DCOMPLEX : + { + double* betap = (double*) bli_obj_buffer( beta ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + cblas_zdscal( mm, *betap, xp, incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_scalv( + f77_int mm, + obj_t* beta, + obj_t* x, + f77_int incx, + num_t dt +){ + switch( dt ) { + case BLIS_FLOAT : + { + float* betap = (float*) bli_obj_buffer( beta ); + float* xp = (float*) bli_obj_buffer( x ); + sscal_( &mm, betap, xp, &incx ); + break; + } + case BLIS_DOUBLE : + { + double* betap = (double*) bli_obj_buffer( beta ); + double* xp = (double*) bli_obj_buffer( x ); + dscal_( &mm, betap, xp, &incx ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cscal_( &mm, betap, xp, &incx ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + zscal_( &mm, betap, xp, &incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_cscalv( + f77_int mm, + obj_t* beta, + obj_t* x, + f77_int incx, + num_t dt +){ + switch( dt ) { + case BLIS_SCOMPLEX : + { + float* betap = (float*) bli_obj_buffer( beta ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + csscal_( &mm, betap, xp, &incx ); + break; + } + case BLIS_DCOMPLEX : + { + double* betap = (double*) bli_obj_buffer( beta ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + zdscal_( &mm, betap, xp, &incx ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_scalv( + test_params_t* params, + iface_t iface, + obj_t* beta, + obj_t* x, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_scalv_impl( iface, beta, x ); + } + else { /*CLBAS || BLAS */ + dim_t m = bli_obj_vector_dim( x ); + f77_int incx = bli_obj_vector_inc( x ); + + if(bli_obj_has_conj(beta)) { + conjugate_tensor(beta, dt); + bli_obj_set_conj( BLIS_NO_CONJUGATE, beta ); + } + + if( params->mixed_precision == 0 ) { + if( params->api == API_CBLAS ) { + cblas_scalv( m, beta, x, incx, dt ); + } else { + blas_scalv( m, beta, x, incx, dt ); + } + } + else{ + if( params->api == API_CBLAS ) { + cblas_cscalv( m, beta, x, incx, dt ); + } else { + blas_cscalv( m, beta, x, incx, dt ); + } + } + } + return ; +} + +double libblis_ref_scalv( + test_params_t* params, + obj_t* beta, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_scalv_check( params, beta, y, y_save); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_iscalv_check( params, beta, y, y_save); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_scalv( + test_params_t* params, + iface_t iface, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0 ; i < n_repeats ; i++) { + bli_copyv( y_orig, r ); + libblis_test_scalv_impl( iface, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_scalv_md ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t dt_beta, dt_y; + dim_t m; + conj_t conjbeta; + obj_t beta, y; + obj_t y_save; + double resid = 0.0; + obj_t dbeta; + + // Decode the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &dt_y ); + bli_param_map_char_to_blis_dt( dc_str[1], &dt_beta ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjbeta ); + + // Create test scalars. + bli_obj_scalar_init_detached( dt_beta, &beta ); + bli_obj_scalar_init_detached( dt_beta, &dbeta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, dt_y, sc_str[0], m, &y ); + libblis_test_vobj_create( params, dt_y, sc_str[0], m, &y_save ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &beta ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize x. + libblis_test_vobj_randomize( params, FALSE, &y ); + } + else{ + if ( bli_obj_is_real( &beta ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize x. + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjbeta, &beta ); + + bli_copysc( &beta, &dbeta ); + + libblis_api_scalv( params, iface, &dbeta, &y, dt_y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, dt_y, sc_str[0], m, &r ); + + + resid = libblis_test_bitrp_scalv( params, iface, &beta, &y, + &y_save, &r, dt_y); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_scalv( params, &beta, &y, &y_save); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return resid; +} + +double libblis_test_op_scalv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + conj_t conjbeta; + obj_t beta, y; + obj_t y_save; + double resid = 0.0; + obj_t dbeta; + + // Use a different function to handle mixed datatypes. + if ( params->mixed_domain || params->mixed_precision ) + { + resid = libblis_test_op_scalv_md( params, iface, dc_str, + pc_str, sc_str, dim, alpv ); + return resid; + } + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjbeta ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + bli_obj_scalar_init_detached( datatype, &dbeta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[0], m, &y_save ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &beta ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize x. + libblis_test_vobj_randomize( params, FALSE, &y ); + } + else{ + if ( bli_obj_is_real( &beta ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize x. + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjbeta, &beta ); + + bli_copysc( &beta, &dbeta ); + + libblis_api_scalv( params, iface, &dbeta, &y, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[0], m, &r ); + + + resid = libblis_test_bitrp_scalv( params, iface, &beta, &y, + &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_scalv( params, &beta, &y, &y_save); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_scalv_impl ( + iface_t iface, + obj_t* beta, + obj_t* y +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_scalv( beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_scalv_check ( + test_params_t* params, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t norm_y_r; + obj_t nbeta; + + obj_t y2; + + double junk; + + double resid = 0.0; + + // + // Pre-conditions: + // - y_orig is randomized. + // Note: + // - beta should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := conjbeta(beta) * y_orig + // + // is functioning correctly if + // + // normfv( y + -conjbeta(beta) * y_orig ) + // + // is negligible. + // + + bli_obj_create( dt, m, 1, 0, 0, &y2 ); + bli_copyv( y_orig, &y2 ); + + bli_obj_scalar_init_detached( dt, &nbeta ); + bli_obj_scalar_init_detached( dt_real, &norm_y_r ); + + bli_copysc( beta, &nbeta ); + bli_mulsc( &BLIS_MINUS_ONE, &nbeta ); + + bli_scalv( &nbeta, &y2 ); + bli_addv( &y2, y ); + + bli_normfv( y, &norm_y_r ); + + bli_getsc( &norm_y_r, &resid, &junk ); + + bli_obj_free( &y2 ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_scalv.h b/gtestsuite/src/test_scalv.h new file mode 100644 index 000000000..947b43246 --- /dev/null +++ b/gtestsuite/src/test_scalv.h @@ -0,0 +1,16 @@ +#ifndef TEST_SCALV_H +#define TEST_SCALV_H + +#include "blis_test.h" + +double libblis_test_iscalv_check + ( + test_params_t* params, + obj_t* beta, + obj_t* x, + obj_t* x_orig + ); + +double libblis_check_nan_scalv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SCALV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_setm.cpp b/gtestsuite/src/test_setm.cpp new file mode 100644 index 000000000..7986fb124 --- /dev/null +++ b/gtestsuite/src/test_setm.cpp @@ -0,0 +1,205 @@ +#include "blis_test.h" +#include "blis_utils.h" + +// Local prototypes. +void libblis_test_setm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_setm_impl ( + iface_t iface, + obj_t* beta, + obj_t* x +); + +double libblis_test_setm_check ( + test_params_t* params, + obj_t* beta, + obj_t* x +); + +double libblis_test_bitrp_setm( + test_params_t* params, + iface_t iface, + obj_t* beta, + obj_t* x, + obj_t* r, + num_t dt +){ + + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + libblis_test_setm_impl( iface, beta, r ); + resid = libblis_test_bitrp_matrix(x, r, dt); + } + + return resid; +} + +double libblis_test_op_setm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m, n; + obj_t beta; + obj_t x; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &x ); + + // Initialize beta to unit. + bli_copysc( &BLIS_ONE, &beta ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Randomize x. + libblis_test_mobj_randomize( params, FALSE, &x ); + } + else { + libblis_test_mobj_irandomize( params, &x ); + } + + libblis_test_setm_impl( iface, &beta, &x ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + + resid = libblis_test_bitrp_setm( params, iface, &beta, &x, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_test_setm_check( params, &beta, &x ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_setm_impl ( + iface_t iface, + obj_t* beta, + obj_t* x +){ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_setm( beta, x ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_setm_check( + test_params_t* params, + obj_t* beta, + obj_t* x +){ + num_t dt_x = bli_obj_dt( x ); + dim_t m_x = bli_obj_length( x ); + dim_t n_x = bli_obj_width( x ); + inc_t rs_x = bli_obj_row_stride( x ); + inc_t cs_x = bli_obj_col_stride( x ); + void* buf_x = (void*)bli_obj_buffer_at_off( x ); + void* buf_beta = (void*)bli_obj_buffer_for_1x1( dt_x, beta ); + dim_t i, j; + double resid = 0.0; + + // The easiest way to check that setm was successful is to confirm + // that each element of x is equal to beta. + + if ( bli_obj_is_float( x ) ) { + float* beta_cast = (float*)buf_beta; + float* buf_x_cast = (float*)buf_x; + float* chi1; + + for ( j = 0; j < n_x; ++j ) { + for ( i = 0; i < m_x; ++i ) { + chi1 = buf_x_cast + (i )*rs_x + (j )*cs_x; + if ( !bli_seq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + } + } + } + else if ( bli_obj_is_double( x ) ) { + double* beta_cast = (double*)buf_beta; + double* buf_x_cast = (double*)buf_x; + double* chi1; + + for ( j = 0; j < n_x; ++j ) { + for ( i = 0; i < m_x; ++i ) { + chi1 = buf_x_cast + (i )*rs_x + (j )*cs_x; + if ( !bli_deq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + } + } + } + else if ( bli_obj_is_scomplex( x ) ) { + scomplex* beta_cast = (scomplex*)buf_beta; + scomplex* buf_x_cast = (scomplex*)buf_x; + scomplex* chi1; + + for ( j = 0; j < n_x; ++j ) { + for ( i = 0; i < m_x; ++i ) { + chi1 = buf_x_cast + (i )*rs_x + (j )*cs_x; + if ( !bli_ceq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + } + } + } + else /* if ( bli_obj_is_dcomplex( x ) )*/ { + dcomplex* beta_cast = (dcomplex*)buf_beta; + dcomplex* buf_x_cast = (dcomplex*)buf_x; + dcomplex* chi1; + + for ( j = 0; j < n_x; ++j ) { + for ( i = 0; i < m_x; ++i ) { + chi1 = buf_x_cast + (i )*rs_x + (j )*cs_x; + if ( !bli_zeq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + } + } + } + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_setv.cpp b/gtestsuite/src/test_setv.cpp new file mode 100644 index 000000000..f76db8a3f --- /dev/null +++ b/gtestsuite/src/test_setv.cpp @@ -0,0 +1,201 @@ +#include "blis_test.h" +#include "blis_utils.h" + +// Local prototypes. +void libblis_test_setv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_setv_impl ( + iface_t iface, + obj_t* beta, + obj_t* x +); + +double libblis_test_setv_check ( + test_params_t* params, + obj_t* beta, + obj_t* x +); + +double libblis_ref_setv( + test_params_t* params, + obj_t* beta, + obj_t* x +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_setv_check( params, beta, x ); + } + return resid; +} + +double libblis_test_bitrp_setv( + test_params_t* params, + iface_t iface, + obj_t* beta, + obj_t* x, + obj_t* r, + num_t dt +){ + + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + libblis_test_setv_impl( iface, beta, r ); + resid = libblis_test_bitrp_matrix(x, r, dt); + } + + return resid; +} + +double libblis_test_op_setv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m; + obj_t beta; + obj_t x; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + + // Initialize beta to unit. + bli_copysc( &BLIS_ONE, &beta ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + } + else { + libblis_test_vobj_irandomize( params, &x ); + } + + libblis_test_setv_impl( iface, &beta, &x ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[0], m, &r ); + + resid = libblis_test_bitrp_setv( params, iface, &beta, &x, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_setv( params, &beta, &x); + } +#endif + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + + return abs(resid); +} + +void libblis_test_setv_impl ( + iface_t iface, + obj_t* beta, + obj_t* x +) { + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_setv( beta, x ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_setv_check ( + test_params_t* params, + obj_t* beta, + obj_t* x +) { + num_t dt_x = bli_obj_dt( x ); + dim_t m_x = bli_obj_vector_dim( x ); + inc_t inc_x = bli_obj_vector_inc( x ); + void* buf_x = (void*)bli_obj_buffer_at_off( x ); + void* buf_beta = (void*)bli_obj_buffer_for_1x1( dt_x, beta ); + dim_t i; + + double resid = 0.0; + + // + // The easiest way to check that setv was successful is to confirm + // that each element of x is equal to beta. + // + + if ( bli_obj_is_float( x ) ) { + float* chi1 = (float*)buf_x; + float* beta_cast = (float*)buf_beta; + + for ( i = 0; i < m_x; ++i ) { + if ( !bli_seq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + chi1 += inc_x; + } + } + else if ( bli_obj_is_double( x ) ) { + double* chi1 = (double*)buf_x; + double* beta_cast = (double*)buf_beta; + + for ( i = 0; i < m_x; ++i ) { + if ( !bli_deq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + chi1 += inc_x; + } + } + else if ( bli_obj_is_scomplex( x ) ) { + scomplex* chi1 = (scomplex*)buf_x; + scomplex* beta_cast = (scomplex*)buf_beta; + + for ( i = 0; i < m_x; ++i ) { + if ( !bli_ceq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + chi1 += inc_x; + } + } + else /* if ( bli_obj_is_dcomplex( x ) ) */{ + dcomplex* chi1 = (dcomplex*)buf_x; + dcomplex* beta_cast = (dcomplex*)buf_beta; + + for ( i = 0; i < m_x; ++i ) { + if ( !bli_zeq( *chi1, *beta_cast ) ) { + resid = 1.0; + return resid; + } + chi1 += inc_x; + } + } + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_subm.cpp b/gtestsuite/src/test_subm.cpp new file mode 100644 index 000000000..d4c2c664b --- /dev/null +++ b/gtestsuite/src/test_subm.cpp @@ -0,0 +1,235 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_subm.h" + +// Local prototypes. +void libblis_test_subm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_subm_impl ( + iface_t iface, + obj_t* x, + obj_t* y +); + +double libblis_test_subm_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +); + +double libblis_ref_subm( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_subm_check( params, alpha, beta, x, y ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isubm_check( params, x, y, y_orig); + } + else { + resid = libblis_test_matrix_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_subm( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( y_orig, r ); + libblis_test_subm_impl( iface, x, r ); + resid = libblis_test_bitrp_matrix(y, r, dt); + } + return resid; +} + +double libblis_test_op_subm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +) { + num_t datatype; + dim_t m, n; + trans_t transx; + obj_t alpha, beta; + obj_t x, y, y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_trans( pc_str[0], &transx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transx, + sc_str[0], m, n, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &y_save ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Initialize alpha and beta. + bli_setsc( -1.0, -1.0, &alpha ); + bli_setsc( 3.0, 3.0, &beta ); + + // Randomize x. + bli_setm( &alpha, &x ); + bli_setm( &beta, &y ); + } + else { + libblis_test_mobj_irandomize( params, &x ); + libblis_test_mobj_irandomize( params, &y ); + } + + // Apply the parameters. + bli_obj_set_conjtrans( transx, &x ); + + //Copy c to c_save + bli_copym( &y, &y_save ); + + libblis_test_subm_impl( iface, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, n, &r ); + resid = libblis_test_bitrp_subm( params, iface, &x, &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_subm( params, &alpha, &beta, &x, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_subm_impl ( + iface_t iface, + obj_t* x, + obj_t* y +) { + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_subm( x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_subm_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + dim_t m = bli_obj_length( y ); + dim_t n = bli_obj_width( y ); + + conj_t conjx = bli_obj_conj_status( x ); + + obj_t aminusb; + obj_t alpha_conj; + obj_t norm_r, m_r, n_r, temp_r; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is set to alpha. + // - y_orig is set to beta. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig - conjx(x) + // + // is functioning correctly if + // + // normfm(y) - sqrt( absqsc( beta - conjx(alpha) ) * m * n ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt, &aminusb ); + bli_obj_scalar_init_detached( dt_real, &temp_r ); + bli_obj_scalar_init_detached( dt_real, &norm_r ); + bli_obj_scalar_init_detached( dt_real, &m_r ); + bli_obj_scalar_init_detached( dt_real, &n_r ); + + bli_obj_scalar_init_detached_copy_of( dt, conjx, alpha, &alpha_conj ); + + bli_normfm( y, &norm_r ); + + bli_copysc( beta, &aminusb ); + bli_subsc( &alpha_conj, &aminusb ); + + bli_setsc( ( double )m, 0.0, &m_r ); + bli_setsc( ( double )n, 0.0, &n_r ); + + bli_absqsc( &aminusb, &temp_r ); + bli_mulsc( &m_r, &temp_r ); + bli_mulsc( &n_r, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + bli_subsc( &temp_r, &norm_r ); + + bli_getsc( &norm_r, &resid, &junk ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_subm.h b/gtestsuite/src/test_subm.h new file mode 100644 index 000000000..59513da64 --- /dev/null +++ b/gtestsuite/src/test_subm.h @@ -0,0 +1,16 @@ +#ifndef TEST_SUBM_H +#define TEST_SUBM_H + +#include "blis_test.h" + +double libblis_test_isubm_check + ( + test_params_t* params, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_subm( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SUBM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_subv.cpp b/gtestsuite/src/test_subv.cpp new file mode 100644 index 000000000..0f15dd4ef --- /dev/null +++ b/gtestsuite/src/test_subv.cpp @@ -0,0 +1,218 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_subv.h" + +// Local prototypes. +void libblis_test_subv_deps(thread_data_t* tdata, + test_params_t* params, test_op_t* op ); + +void libblis_test_subv_impl(iface_t iface, obj_t* x, obj_t* y ); + +double libblis_test_subv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +); + +double libblis_ref_subv( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_subv_check(params, alpha, beta, x, y ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isubv_check(params, alpha, beta, x, y, y_save); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_subv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_subv_impl( iface, x, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_subv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t alpha, beta; + obj_t x, y, y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + // Initialize alpha and beta. + bli_setsc( 1.0, 1.0, &alpha ); + bli_setsc( 3.0, 3.0, &beta ); + + // Set x and y to alpha and beta, respectively. + bli_setv( &alpha, &x ); + bli_setv( &beta, &y ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + bli_copyv( &y, &y_save ); + + libblis_test_subv_impl( iface, &x, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_subv( params, iface, &x, &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_subv( params, &alpha, &beta, &x, &y, &y_save); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_subv_impl ( + iface_t iface, + obj_t* x, + obj_t* y +){ + + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_subv( x, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_subv_check ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y +) { + num_t dt = bli_obj_dt( x ); + num_t dt_real = bli_obj_dt_proj_to_real( x ); + dim_t m = bli_obj_vector_dim( x ); + + conj_t conjx = bli_obj_conj_status( x ); + + obj_t aminusb; + obj_t alpha_conj; + obj_t norm_r, m_r, temp_r; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is set to alpha. + // - y_orig is set to beta. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := y_orig - conjx(x) + // + // is functioning correctly if + // + // normfv(y) - sqrt( absqsc( beta - conjx(alpha) ) * m ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt, &aminusb ); + bli_obj_scalar_init_detached( dt_real, &temp_r ); + bli_obj_scalar_init_detached( dt_real, &norm_r ); + bli_obj_scalar_init_detached( dt_real, &m_r ); + + bli_obj_scalar_init_detached_copy_of( dt, conjx, alpha, &alpha_conj ); + + bli_normfv( y, &norm_r ); + + bli_copysc( beta, &aminusb ); + bli_subsc( &alpha_conj, &aminusb ); + + bli_setsc( ( double )m, 0.0, &m_r ); + + bli_absqsc( &aminusb, &temp_r ); + bli_mulsc( &m_r, &temp_r ); + bli_sqrtsc( &temp_r, &temp_r ); + bli_subsc( &temp_r, &norm_r ); + + bli_getsc( &norm_r, &resid, &junk ); + + return resid; +} + + diff --git a/gtestsuite/src/test_subv.h b/gtestsuite/src/test_subv.h new file mode 100644 index 000000000..0733e50da --- /dev/null +++ b/gtestsuite/src/test_subv.h @@ -0,0 +1,18 @@ +#ifndef TEST_SUBV_H +#define TEST_SUBV_H + +#include "blis_test.h" + +double libblis_test_isubv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* beta, + obj_t* x, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_subv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SUBV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_symm.cpp b/gtestsuite/src/test_symm.cpp new file mode 100644 index 000000000..40fa863a9 --- /dev/null +++ b/gtestsuite/src/test_symm.cpp @@ -0,0 +1,551 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_symm.h" + +using namespace std; + +// Local prototypes. +void libblis_test_symm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_symm_impl( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +); + +double libblis_test_symm_check( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_symm( + side_t side, + uplo_t uploa, + f77_int mm, + f77_int nn, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_SIDE cblas_side; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if(bli_is_upper(uploa)) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if(bli_is_left(side)) + cblas_side = CblasLeft; + else + cblas_side = CblasRight; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_ssymm( cblas_order, cblas_side, cblas_uplo, mm, nn, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dsymm( cblas_order, cblas_side, cblas_uplo, mm, nn, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_csymm( cblas_order, cblas_side, cblas_uplo, mm, nn, alphap, + ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zsymm( cblas_order, cblas_side, cblas_uplo, mm, nn, alphap, + ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return 0; +} + +double blas_symm( + side_t side, + uplo_t uploa, + f77_int mm, + f77_int nn, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + + f77_char f77_side; + f77_char f77_uploa; + + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + ssymm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dsymm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + csymm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zsymm_( &f77_side, &f77_uploa, &mm, &nn, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_symm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_symm_impl( iface, side, alpha, a, b, beta, c ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( c ); + f77_int nn = bli_obj_width( c ); + f77_int lda, ldb, ldc; + + if( bli_obj_row_stride( c ) == 1 ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + ldc = ldc + params->ld[2]; + } + + if(params->api == API_CBLAS) { + cblas_symm( side, uploa, mm, nn, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } else { /**/ + if( bli_obj_row_stride( c ) == 1 ) { + blas_symm( side, uploa, mm, nn, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + else { + if( side == BLIS_LEFT) + side = BLIS_RIGHT; + else if(side == BLIS_RIGHT) + side = BLIS_LEFT; + + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + blas_symm( side, uploa, nn, mm, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + } + } + return ; +} + +double libblis_ref_symm( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_save, + num_t dt +) { + + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_symm(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_symm_check( params, side, alpha, a, b, beta, c, c_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isymm_check( params, side, alpha, a, b, beta, c, c_save ); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_symm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_symm_impl( iface, side, alpha, a, b, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_symm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, n; + dim_t mn_side; + side_t side; + uplo_t uploa; + obj_t alpha, a, b, beta, c; + obj_t c_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_side( pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( pc_str[1], &uploa ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + bli_set_dim_with_side( side, m, n, &mn_side ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], mn_side, mn_side, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, n, &b ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_SYMMETRIC, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, (betv.real/1.2), &beta ); + } + // Randomize A, B, and C, and save C. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_mobj_randomize( params, TRUE, &b ); + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)1.0; //alpv.real; + int32_t y = (int32_t)1.0; //betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + libblis_test_mobj_irandomize( params, &c ); + } + + if ((params->nanf) && (betv.real == 0) ) { + test_fillbuffmem(&c, datatype ); + } + + // Randomize A, make it densely symmetric, and zero the unstored triangle + // to ensure the implementation reads only from the stored region. + bli_mksymm( &a ); + bli_mktrim( &a ); + + //Copy c to c_save + bli_copym( &c, &c_save ); + + libblis_api_symm(params, iface, side, &alpha, &a, &b, &beta, &c, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + resid = libblis_test_bitrp_symm( params, iface, side,&alpha, &a, &b, + &beta, &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_symm(params, side, &alpha, &a, &b, &beta, + &c, &c_save, datatype ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_symm_impl ( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +){ + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_symm( side, alpha, a, b, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_symm_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +) { + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t n = bli_obj_width( c ); + + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized and symmetric. + // - b is randomized. + // - c_orig is randomized. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * conja(A) * transb(B) (side = left) + // C := beta * C_orig + alpha * transb(B) * conja(A) (side = right) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // + // z = ( beta * C_orig + alpha * conja(A) * transb(B) ) * t (side = left) + // = beta * C_orig * t + alpha * conja(A) * transb(B) * t + // = beta * C_orig * t + alpha * conja(A) * w + // = beta * C_orig * t + z + // + // z = ( beta * C_orig + alpha * transb(B) * conja(A) ) * t (side = right) + // = beta * C_orig * t + alpha * transb(B) * conja(A) * t + // = beta * C_orig * t + alpha * transb(B) * w + // = beta * C_orig * t + z + + bli_obj_scalar_init_detached( dt_real, &norm ); + + if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, m, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + else // else if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, n, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + if ( bli_is_left( side ) ) + { + bli_gemv( &BLIS_ONE, b, &t, &BLIS_ZERO, &w ); + bli_symv( alpha, a, &w, &BLIS_ZERO, &z ); + } + else + { + bli_symv( &BLIS_ONE, a, &t, &BLIS_ZERO, &w ); + bli_gemv( alpha, b, &w, &BLIS_ZERO, &z ); + } + + bli_gemv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_symm.h b/gtestsuite/src/test_symm.h new file mode 100644 index 000000000..b47940980 --- /dev/null +++ b/gtestsuite/src/test_symm.h @@ -0,0 +1,20 @@ +#ifndef TEST_SYMM_H +#define TEST_SYMM_H + +#include "blis_test.h" + +double libblis_test_isymm_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_symm(obj_t* c, num_t dt ); + +#endif /* TEST_SYMM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_symv.cpp b/gtestsuite/src/test_symv.cpp new file mode 100644 index 000000000..6c0c68a73 --- /dev/null +++ b/gtestsuite/src/test_symv.cpp @@ -0,0 +1,497 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_symv.h" + +// Local prototypes. +void libblis_test_symv_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_symv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +); + +double libblis_test_symv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +void cblas_symv( + uplo_t uploa, + f77_int m, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uplo ; + + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploa ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* betap = (float*) bli_obj_buffer( beta ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_ssymv(cblas_order, cblas_uplo, m, *alphap, ap, lda, xp, incx, + *betap, yp, incy); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* betap = (double*) bli_obj_buffer( beta ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dsymv(cblas_order, cblas_uplo, m, *alphap, ap, lda, xp, incx, + *betap, yp, incy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_chemv(cblas_order, cblas_uplo, m, alphap, ap, lda, xp, incx, + betap, yp, incy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zhemv(cblas_order, cblas_uplo, m, alphap, ap, lda, xp, incx, + betap, yp, incy); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_symv( + f77_char f77_uploa, + f77_int m, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + obj_t* beta, + obj_t* y, + f77_int incy, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* betap = (float*) bli_obj_buffer( beta ); + float* yp = (float*) bli_obj_buffer( y ); + ssymv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* betap = (double*) bli_obj_buffer( beta ); + double* yp = (double*) bli_obj_buffer( y ); + dsymv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + chemv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zhemv_(&f77_uploa, &m, alphap, ap, (f77_int*)&lda, xp, &incx, + betap, yp, &incy); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_symv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +){ + + if(params->api == API_BLIS) { + libblis_test_symv_impl( iface, alpha, a, x, beta, y); + } + else { /*CLBAS || BLAS */ + num_t dt = bli_obj_dt( a ); + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if( bli_obj_has_conj(a) ) { + conjugate_tensor(a, dt); + } + if( bli_obj_has_conj(x) ) { + conjugate_tensor(x, dt); + } + + if(params->api == API_CBLAS) { + cblas_symv(uploa, mm, alpha, a, lda, x, incx, beta, y, incy, dt ); + } + else { /**/ + f77_char f77_uploa; + if ( bli_obj_row_stride( a ) == 1 ){ + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_symv(f77_uploa, mm, alpha, a, lda, x, incx, beta, y, incy, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_symv(f77_uploa, mm, alpha, a, lda, x, incx, beta, y, incy, dt ); + } + } + } + return ; +} + +double libblis_ref_symv( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_symv_check( params, alpha, a, x, beta, y, y_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isymv_check( params, alpha, a, x, beta, y, y_orig); + } + else { + resid = libblis_test_vector_check(params, y); + } + } + return resid; +} + +double libblis_test_bitrp_symv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_symv_impl( iface, alpha, a, x, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_symv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + conj_t conja; + conj_t conjx; + obj_t alpha, a, x, beta, y; + obj_t y_save; + double resid = 0.0; + obj_t aa, xx; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_conj( pc_str[1], &conja ); + bli_param_map_char_to_blis_conj( pc_str[2], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &aa ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &xx ); + libblis_test_vobj_create( params, datatype, + sc_str[2], m, &y ); + libblis_test_vobj_create( params, datatype, + sc_str[2], m, &y_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_SYMMETRIC, &a ); + bli_obj_set_uplo( uploa, &a ); + + bli_obj_set_struc( BLIS_SYMMETRIC, &aa ); + bli_obj_set_uplo( uploa, &aa ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &y ) ){ + bli_setsc( 1.0, 0.0, &alpha ); + bli_setsc( -1.0, 0.0, &beta ); + } + else{ + bli_setsc( 0.5, 0.5, &alpha ); + bli_setsc( -0.5, 0.5, &beta ); + } + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + } + else { + int32_t xx = (int32_t) 1.0; + int32_t yy = (int32_t)-1.0; + if ( bli_obj_is_real( &y ) ){ + bli_setsc( xx, 0.0, &alpha ); + bli_setsc( yy, 0.0, &beta ); + } + else{ + xx = (int32_t)(xx/0.8); + yy = (int32_t)(yy/1.5); + bli_setsc( xx, (xx+yy), &alpha ); + bli_setsc( yy, (xx-yy), &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + // Randomize A, make it densely symmetric, and zero the unstored triangle + // to ensure the implementation reads only from the stored region. + bli_mksymm( &a ); + bli_mktrim( &a ); + + // Randomize x and y, and save y. + bli_copyv( &y, &y_save ); + + bli_copym( &a, &aa ); + bli_copyv( &x, &xx ); + + // Apply the remaining parameters. + bli_obj_set_conj( conja, &a ); + bli_obj_set_conj( conjx, &x ); + + bli_obj_set_conj( conja, &aa ); + bli_obj_set_conj( conjx, &xx ); + + libblis_api_symv(params, iface, &alpha, &aa, &xx, &beta, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[2], m, &r ); + + resid = libblis_test_bitrp_symv( params, iface, &alpha, &a, &x, + &beta, &y, &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_symv( params, &alpha, &a, &x, &beta, &y, &y_save); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_symv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_symv( alpha, a, x, beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_symv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t v; + obj_t norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized and symmetric. + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + alpha * conja(A) * conjx(x) + // + // is functioning correctly if + // + // normfv( y - v ) + // + // is negligible, where + // + // v = beta * y_orig + alpha * conja(A_dense) * x + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &v ); + + bli_copyv( y_orig, &v ); + + bli_mksymm( a ); + bli_obj_set_struc( BLIS_GENERAL, a ); + bli_obj_set_uplo( BLIS_DENSE, a ); + + bli_gemv( alpha, a, x, beta, &v ); + + bli_subv( &v, y ); + bli_normfv( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &v ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_symv.h b/gtestsuite/src/test_symv.h new file mode 100644 index 000000000..b8e1b6e51 --- /dev/null +++ b/gtestsuite/src/test_symv.h @@ -0,0 +1,19 @@ +#ifndef TEST_SYMV_H +#define TEST_SYMV_H + +#include "blis_test.h" + +double libblis_test_isymv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_symv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SYMV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_syr.cpp b/gtestsuite/src/test_syr.cpp new file mode 100644 index 000000000..83319ecca --- /dev/null +++ b/gtestsuite/src/test_syr.cpp @@ -0,0 +1,420 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syr.h" + +// Local prototypes. +void libblis_test_syr_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_syr_impl ( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a +); + +double libblis_test_syr_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +); + +void cblas_syr( + uplo_t uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* a, + f77_int lda, + num_t dt +){ + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_ORDER cblas_order = CblasColMajor; + + if( bli_is_upper( uploa ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + cblas_ssyr(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + cblas_dsyr(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + case BLIS_SCOMPLEX : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cblas_cher(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + case BLIS_DCOMPLEX : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + cblas_zher(cblas_order, cblas_uplo, m, *alphap, xp, incx, ap, lda); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_syr( + f77_char f77_uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* a, + f77_int lda, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + ssyr_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + dsyr_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + case BLIS_SCOMPLEX : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cher_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + case BLIS_DCOMPLEX : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + zher_(&f77_uploa, &m, alphap, xp, &incx, ap, (f77_int*)&lda ); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_syr( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_syr_impl( iface, alpha, x, a ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if(params->api == API_CBLAS) { + cblas_syr(uploa, mm, alpha, x, incx, a, lda, dt ); + } + else { /**/ + f77_char f77_uploa; + if ( bli_obj_row_stride( a ) == 1 ){ + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_syr(f77_uploa, mm, alpha, x, incx, a, lda, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_syr(f77_uploa, mm, alpha, x, incx, a, lda, dt ); + } + } + } + return ; +} + +double libblis_ref_syr( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_syr_check( params, alpha, x, a, a_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isyr_check( params, alpha, x, a, a_orig ); + } + else { + resid = libblis_test_vector_check(params, a); + } + } + return resid; +} + +double libblis_test_bitrp_syr( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( a_orig, r ); + libblis_test_syr_impl( iface, alpha, x, r ); + resid = libblis_test_bitrp_vector(a, r, dt); + } + return resid; +} + +double libblis_test_op_syr ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + conj_t conjx; + obj_t alpha, x, a; + obj_t a_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, m, &a_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_SYMMETRIC, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + bli_setsc( alpv.real, 0.0, &alpha ); + // Randomize x. + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_mobj_randomize( params, TRUE, &a ); + } + else{ + int32_t xx = (int32_t)alpv.real; + bli_setsc( (double)xx, (double)0.0, &alpha ); + // Randomize x. + libblis_test_vobj_irandomize( params, &x ); + libblis_test_mobj_irandomize( params, &a ); + } + + // Randomize A, make it densely symmetric, and zero the unstored triangle + // to ensure the implementation is reads only from the stored region. + bli_mksymm( &a ); + bli_mktrim( &a ); + + // Save A and set its structure and uplo properties. + bli_obj_set_struc( BLIS_SYMMETRIC, &a_save ); + bli_obj_set_uplo( uploa, &a_save ); + bli_copym( &a, &a_save ); + bli_mktrim( &a_save ); + + // Apply the remaining parameters. + bli_obj_set_conj( conjx, &x ); + + libblis_api_syr(params, iface, &alpha, &x, &a, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[1], m, m, &r ); + + resid = libblis_test_bitrp_syr( params, iface, &alpha, &x, + &a, &a_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_syr( params, &alpha, &x, &a, &a_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &a, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &a ); + libblis_test_obj_free( &a_save ); + + return abs(resid); +} + +void libblis_test_syr_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* a +){ + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_syr( alpha, x, a ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_syr_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig +){ + num_t dt = bli_obj_dt( a ); + num_t dt_real = bli_obj_dt_proj_to_real( a ); + + dim_t m_a = bli_obj_length( a ); + + obj_t xt, t, v, w; + obj_t rho, norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - a is randomized and symmetric. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // A := A_orig + alpha * conjx(x) * conjx(x)^T + // + // is functioning correctly if + // + // normfv( v - w ) + // + // is negligible, where + // + // v = A * t + // w = ( A_orig + alpha * conjx(x) * conjx(x)^T ) * t + // = A_orig * t + alpha * conjx(x) * conjx(x)^T * t + // = A_orig * t + alpha * conjx(x) * rho + // = A_orig * t + w + // + + bli_mksymm( a ); + bli_mksymm( a_orig ); + bli_obj_set_struc( BLIS_GENERAL, a ); + bli_obj_set_struc( BLIS_GENERAL, a_orig ); + bli_obj_set_uplo( BLIS_DENSE, a ); + bli_obj_set_uplo( BLIS_DENSE, a_orig ); + + bli_obj_scalar_init_detached( dt, &rho ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m_a, 1, 0, 0, &t ); + bli_obj_create( dt, m_a, 1, 0, 0, &v ); + bli_obj_create( dt, m_a, 1, 0, 0, &w ); + + bli_obj_alias_to( x, &xt ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, a, &t, &BLIS_ZERO, &v ); + + bli_dotv( &xt, &t, &rho ); + bli_mulsc( alpha, &rho ); + bli_scal2v( &rho, x, &w ); + bli_gemv( &BLIS_ONE, a_orig, &t, &BLIS_ONE, &w ); + + bli_subv( &w, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_syr.h b/gtestsuite/src/test_syr.h new file mode 100644 index 000000000..33b592a93 --- /dev/null +++ b/gtestsuite/src/test_syr.h @@ -0,0 +1,17 @@ +#ifndef TEST_SYR_H +#define TEST_SYR_H + +#include "blis_test.h" + +double libblis_test_isyr_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* a, + obj_t* a_orig + ); + +double libblis_check_nan_syr( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SYR_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_syr2.cpp b/gtestsuite/src/test_syr2.cpp new file mode 100644 index 000000000..44360fba8 --- /dev/null +++ b/gtestsuite/src/test_syr2.cpp @@ -0,0 +1,496 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syr2.h" + +// Local prototypes. +void libblis_test_syr2_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_syr2_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a +); + +double libblis_test_syr2_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +); + +void cblas_syr2( + uplo_t uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* a, + f77_int lda, + num_t dt +){ + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_ORDER cblas_order; + if ( bli_obj_row_stride( a ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploa ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + cblas_ssyr2(cblas_order, cblas_uplo, m, *alphap, xp, incx, + yp, incy, ap, lda); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + cblas_dsyr2(cblas_order, cblas_uplo, m, *alphap, xp, incx, + yp, incy, ap, lda); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cblas_cher2(cblas_order, cblas_uplo, m, alphap, xp, incx, + yp, incy, ap, lda); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + cblas_zher2(cblas_order, cblas_uplo, m, alphap, xp, incx, + yp, incy, ap, lda); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_syr2( + f77_char f77_uploa, + f77_int m, + obj_t* alpha, + obj_t* x, + f77_int incx, + obj_t* y, + f77_int incy, + obj_t* a, + f77_int lda, + num_t dt +){ + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + float* yp = (float*) bli_obj_buffer( y ); + ssyr2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + double* yp = (double*) bli_obj_buffer( y ); + dsyr2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + scomplex* yp = (scomplex*) bli_obj_buffer( y ); + cher2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + dcomplex* yp = (dcomplex*) bli_obj_buffer( y ); + zher2_(&f77_uploa, &m, alphap, xp, &incx, yp, &incy, ap, (f77_int*)&lda); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_syr2( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_syr2_impl( iface, alpha, x, y, a ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int incy = bli_obj_vector_inc( y ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + if(params->api == API_CBLAS) { + cblas_syr2(uploa, mm, alpha, x, incx, y, incy, a, lda, dt ); + } + else { /**/ + f77_char f77_uploa; + if ( bli_obj_row_stride( a ) == 1 ){ + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_syr2(f77_uploa, mm, alpha, x, incx, y, incy, a, lda, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + conjugate_tensor(x, dt); + conjugate_tensor(y, dt); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + blas_syr2(f77_uploa, mm, alpha, x, incx, y, incy, a, lda, dt ); + } + } + } + return ; +} + +double libblis_ref_syr2( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_syr2_check( params, alpha, x, y, a, a_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isyr2_check( params, alpha, x, y, a, a_orig); + } + else { + resid = libblis_test_matrix_check(params, a); + } + } + return resid; +} + +double libblis_test_bitrp_syr2( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( a_orig, r ); + bli_mksymm( r ); + bli_mktrim( r ); + libblis_test_syr2_impl( iface, alpha, x, y, r); + resid = libblis_test_bitrp_matrix(a, r, dt); + } + return resid; +} + +double libblis_test_op_syr2 ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + conj_t conjx, conjy; + obj_t alpha, x, y, a; + obj_t a_save; + double resid = 0.0; + obj_t xx, yy; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_conj( pc_str[1], &conjx ); + bli_param_map_char_to_blis_conj( pc_str[2], &conjy ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[0], m, &xx ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &yy ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, m, &a_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_SYMMETRIC, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &x ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + } + else { + bli_setsc( alpv.real, alpv.imag, &alpha ); + } + // Randomize x and y. + libblis_test_vobj_randomize( params, TRUE, &x ); + libblis_test_vobj_randomize( params, TRUE, &y ); + libblis_test_mobj_randomize( params, TRUE, &a ); + } + else{ + int32_t xx = (int32_t)alpv.real; + if ( bli_obj_is_real( &x ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + } + else { + int32_t ax = (int32_t)(xx/0.8); + bli_setsc( (double)xx, (double)ax, &alpha ); + } + + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + libblis_test_mobj_irandomize( params, &a ); + } + + // Randomize A, make it densely symmetric, and zero the unstored triangle + // to ensure the implementation is reads only from the stored region. + bli_mksymm( &a ); + bli_mktrim( &a ); + + // Save A and set its structure and uplo properties. + bli_obj_set_struc( BLIS_SYMMETRIC, &a_save ); + bli_obj_set_uplo( uploa, &a_save ); + bli_copym( &a, &a_save ); + bli_mksymm( &a_save ); + bli_mktrim( &a_save ); + + // Apply the remaining parameters. + bli_obj_set_conj( conjx, &x ); + bli_obj_set_conj( conjy, &y ); + + bli_copyv( &x, &xx ); + bli_copyv( &y, &yy ); + + libblis_api_syr2( params, iface, &alpha, &xx, &yy, &a, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[2], m, m, &r ); + bli_obj_set_struc( BLIS_SYMMETRIC, &r ); + bli_obj_set_uplo( uploa, &r ); + + resid = libblis_test_bitrp_syr2( params, iface, &alpha, &x, &y, + &a, &a_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_syr2( params, &alpha, &x, &y, &a, &a_save); + } +#endif + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &a, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &xx ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &yy ); + libblis_test_obj_free( &a ); + libblis_test_obj_free( &a_save ); + + return abs(resid); +} + +void libblis_test_syr2_impl( + iface_t iface, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_syr2( alpha, x, y, a ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_syr2_check( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig +){ + num_t dt = bli_obj_dt( a ); + num_t dt_real = bli_obj_dt_proj_to_real( a ); + + dim_t m_a = bli_obj_length( a ); + + obj_t xt, yt; + obj_t t, v, w1, w2; + obj_t rho, norm; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - x is randomized. + // - y is randomized. + // - a is randomized and symmetric. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // A := A_orig + alpha * conjx(x) * conjy(y)^T + alpha * conjy(y) * conjx(x)^T + // + // is functioning correctly if + // + // normfv( v - w ) + // + // is negligible, where + // + // v = A * t + // w = ( A_orig + alpha * conjx(x) * conjy(y)^T + alpha * conjy(y) * conjx(x)^T ) * t + // = A_orig * t + alpha * conjx(x) * conjy(y)^T * t + alpha * conjy(y) * conjx(x)^T * t + // = A_orig * t + alpha * conjx(x) * conjy(y)^T * t + alpha * conjy(y) * rho + // = A_orig * t + alpha * conjx(x) * conjy(y)^T * t + w1 + // = A_orig * t + alpha * conjx(x) * rho + w1 + // = A_orig * t + w2 + w1 + // + + bli_mksymm( a ); + bli_mksymm( a_orig ); + bli_obj_set_struc( BLIS_GENERAL, a ); + bli_obj_set_struc( BLIS_GENERAL, a_orig ); + bli_obj_set_uplo( BLIS_DENSE, a ); + bli_obj_set_uplo( BLIS_DENSE, a_orig ); + + bli_obj_scalar_init_detached( dt, &rho ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m_a, 1, 0, 0, &t ); + bli_obj_create( dt, m_a, 1, 0, 0, &v ); + bli_obj_create( dt, m_a, 1, 0, 0, &w1 ); + bli_obj_create( dt, m_a, 1, 0, 0, &w2 ); + + bli_obj_alias_to( x, &xt ); + bli_obj_alias_to( y, &yt ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, a, &t, &BLIS_ZERO, &v ); + + bli_dotv( &xt, &t, &rho ); + bli_mulsc( alpha, &rho ); + bli_scal2v( &rho, y, &w1 ); + + bli_dotv( &yt, &t, &rho ); + bli_mulsc( alpha, &rho ); + bli_scal2v( &rho, x, &w2 ); + + bli_addv( &w2, &w1 ); + + bli_gemv( &BLIS_ONE, a_orig, &t, &BLIS_ONE, &w1 ); + + bli_subv( &w1, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w1 ); + bli_obj_free( &w2 ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_syr2.h b/gtestsuite/src/test_syr2.h new file mode 100644 index 000000000..ce9621f6a --- /dev/null +++ b/gtestsuite/src/test_syr2.h @@ -0,0 +1,18 @@ +#ifndef TEST_SYR2_H +#define TEST_SYR2_H + +#include "blis_test.h" + +double libblis_test_isyr2_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* x, + obj_t* y, + obj_t* a, + obj_t* a_orig + ); + +double libblis_check_nan_syr2( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_SYR2_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_syr2k.cpp b/gtestsuite/src/test_syr2k.cpp new file mode 100644 index 000000000..7092c53cd --- /dev/null +++ b/gtestsuite/src/test_syr2k.cpp @@ -0,0 +1,552 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syr2k.h" + +using namespace std; + +// Local prototypes. +void libblis_test_syr2k_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +); + +double libblis_test_syr2k_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_syr2k( + uplo_t uploc, + trans_t trans, + f77_int mm, + f77_int kk, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uploc; + enum CBLAS_TRANSPOSE cblas_trans; + + if( bli_is_trans( trans ) ) + cblas_trans = CblasTrans; + else + cblas_trans = CblasNoTrans; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if(bli_is_upper(uploc)) + cblas_uploc = CblasUpper; + else + cblas_uploc = CblasLower; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_ssyr2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dsyr2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, *alphap, + ap, lda, bp, ldb, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_csyr2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, alphap, + ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zsyr2k( cblas_order, cblas_uploc, cblas_trans, mm, kk, alphap, + ap, lda, bp, ldb, betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + + return 0; +} + +double blas_syr2k( + uplo_t uploc, + trans_t trans, + f77_int mm, + f77_int kk, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* b, + f77_int ldb, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + + f77_char f77_uploc; + f77_char f77_trans; + + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + bli_param_map_blis_to_netlib_trans( trans, &f77_trans ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + ssyr2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dsyr2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap,(f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + csyr2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zsyr2k_( &f77_uploc, &f77_trans, &mm, &kk, alphap, ap, (f77_int*)&lda, + bp, (f77_int*)&ldb, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_syr2k( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + num_t dt +){ + if(params->api == API_BLIS) { + libblis_test_syr2k_impl( iface, alpha, a, b, beta, c ); + } + else { /*CLBAS || BLAS */ + uplo_t uploc = bli_obj_uplo( c ); + dim_t m = bli_obj_length( c ); + dim_t k = bli_obj_width_after_trans( a ); + trans_t trans = bli_obj_onlytrans_status( a ); + f77_int lda, ldb, ldc; + + if( bli_obj_row_stride( c ) == 1 ) { + lda = bli_obj_col_stride( a ); + ldb = bli_obj_col_stride( b ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldb = bli_obj_row_stride( b ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + ldc = ldc + params->ld[2]; + } + + if(params->api == API_CBLAS) { + cblas_syr2k( uploc, trans, m, k, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } else { + if( bli_obj_row_stride( c ) == 1 ) { + blas_syr2k( uploc, trans, m, k, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + else { + if( uploc == BLIS_UPPER) + uploc = BLIS_LOWER; + else if(uploc == BLIS_LOWER) + uploc = BLIS_UPPER; + + if( trans == BLIS_NO_TRANSPOSE) + trans = BLIS_TRANSPOSE; + else if(trans == BLIS_TRANSPOSE) + trans = BLIS_NO_TRANSPOSE; + + blas_syr2k( uploc, trans, m, k, alpha, a, lda, b, ldb, beta, c, ldc, dt ); + } + } + } + return ; +} + +double libblis_ref_syr2k( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_save, + num_t dt +) { + + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_syr2k(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_syr2k_check( params, alpha, a, b, beta, c, c_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isyr2k_check( params, alpha, a, b, beta, c, c_save ); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_syr2k( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_syr2k_impl( iface, alpha, a, b, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_syr2k ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, k; + uplo_t uploc; + trans_t trans; + obj_t alpha, a, b, beta, c; + obj_t c_save; + double resid = 0.0; + obj_t aa, bb; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploc ); + bli_param_map_char_to_syrk_trans( pc_str[1], &trans ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, trans, + sc_str[1], m, k, &a ); + libblis_test_mobj_create( params, datatype, trans, + sc_str[1], m, k, &aa ); + libblis_test_mobj_create( params, datatype, trans, + sc_str[2], m, k, &b ); + libblis_test_mobj_create( params, datatype, trans, + sc_str[2], m, k, &bb ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_save ); + + // Set the structure and uplo properties of C. + bli_obj_set_struc( BLIS_SYMMETRIC, &c ); + bli_obj_set_uplo( uploc, &c ); + + // Set alpha and beta. + // For syr2k, both alpha and beta may be complex since, unlike her2k, + // C is symmetric in both the real and complex cases. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + // Randomize A, B, and C, and save C. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_mobj_randomize( params, TRUE, &b ); + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)1.0; //alpv.real; + int32_t y = (int32_t)1.0; //betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + libblis_test_mobj_irandomize( params, &c ); + } + + if ((params->nanf) && (betv.real == 0) ) { + test_fillbuffmem(&c, datatype ); + } + + // Randomize A, make it densely symmetric, and zero the unstored triangle + // to ensure the implementation is reads only from the stored region. + bli_mksymm( &c ); + bli_mktrim( &c ); + + // Save C and set its structure and uplo properties. + bli_obj_set_struc( BLIS_SYMMETRIC, &c_save ); + bli_obj_set_uplo( uploc, &c_save ); + bli_copym( &c, &c_save ); + bli_mksymm( &c_save ); + bli_mktrim( &c_save ); + + // Apply the remaining parameters. + bli_copym( &a, &aa ); + bli_copym( &b, &bb ); + + bli_obj_set_conjtrans( trans, &a ); + bli_obj_set_conjtrans( trans, &b ); + + bli_obj_set_conjtrans( trans, &aa ); + bli_obj_set_conjtrans( trans, &bb ); + + libblis_api_syr2k(params, iface, &alpha, &aa, &bb, &beta, &c, datatype ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &r ); + + resid = libblis_test_bitrp_syr2k( params, iface, &alpha, &a, &b, + &beta, &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_syr2k(params, &alpha, &a, &b, &beta, + &c, &c_save, datatype ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &bb ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_syr2k_impl + ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c + ) +{ + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_syr2k( alpha, a, b, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_syr2k_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ) +{ + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t k = bli_obj_width_after_trans( a ); + + obj_t at, bt; + obj_t norm; + obj_t t, v, w1, w2, z; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized. + // - b is randomized. + // - c_orig is randomized and symmetric. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transb(B)^T + alpha * transb(B) * transa(A)^T + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // z = ( beta * C_orig + alpha * transa(A) * transb(B)^T + alpha * transb(B) * transa(A)^T ) * t + // = beta * C_orig * t + alpha * transa(A) * transb(B)^T * t + alpha * transb(B) * transa(A)^T * t + // = beta * C_orig * t + alpha * transa(A) * transb(B)^T * t + alpha * transb(B) * w2 + // = beta * C_orig * t + alpha * transa(A) * w1 + alpha * transb(B) * w2 + // = beta * C_orig * t + alpha * transa(A) * w1 + z + // = beta * C_orig * t + z + // + + bli_obj_alias_with_trans( BLIS_TRANSPOSE, a, &at ); + bli_obj_alias_with_trans( BLIS_TRANSPOSE, b, &bt ); + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, k, 1, 0, 0, &w1 ); + bli_obj_create( dt, k, 1, 0, 0, &w2 ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_symv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + bli_gemv( &BLIS_ONE, &at, &t, &BLIS_ZERO, &w2 ); + bli_gemv( &BLIS_ONE, &bt, &t, &BLIS_ZERO, &w1 ); + bli_gemv( alpha, a, &w1, &BLIS_ZERO, &z ); + bli_gemv( alpha, b, &w2, &BLIS_ONE, &z ); + bli_symv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w1 ); + bli_obj_free( &w2 ); + bli_obj_free( &z ); + + return resid; +} + diff --git a/gtestsuite/src/test_syr2k.h b/gtestsuite/src/test_syr2k.h new file mode 100644 index 000000000..eb32e953a --- /dev/null +++ b/gtestsuite/src/test_syr2k.h @@ -0,0 +1,19 @@ +#ifndef TEST_SYR2K_H +#define TEST_SYR2K_H + +#include "blis_test.h" + +double libblis_test_isyr2k_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_syr2k(obj_t* c, num_t dt ); + +#endif /* TEST_SYR2K_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_syrk.cpp b/gtestsuite/src/test_syrk.cpp new file mode 100644 index 000000000..613271278 --- /dev/null +++ b/gtestsuite/src/test_syrk.cpp @@ -0,0 +1,504 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_syrk.h" + +void libblis_test_syrk_impl ( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c +); + +double libblis_test_syrk_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +double cblas_syrk( + uplo_t uploc, + trans_t transa, + f77_int n, + f77_int k, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_TRANSPOSE cblas_trans; + enum CBLAS_UPLO cblas_uplo; + + if ( bli_obj_row_stride( c ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_upper( uploc ) ) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if( bli_is_trans( transa ) ) + cblas_trans = CblasTrans; + else + cblas_trans = CblasNoTrans; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + cblas_ssyrk( cblas_order, cblas_uplo, cblas_trans, n, k, + *alphap, ap, lda, *betap, cp, ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + cblas_dsyrk( cblas_order, cblas_uplo, cblas_trans, n, k, + *alphap, ap, lda, *betap, cp, ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + cblas_csyrk( cblas_order, cblas_uplo, cblas_trans, n, k, + alphap, ap, lda, betap, cp, ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + cblas_zsyrk( cblas_order, cblas_uplo, cblas_trans, n, k, + alphap, ap, lda, betap, cp, ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_syrk( + uplo_t uploc, + f77_char f77_transa, + f77_int n, + f77_int k, + obj_t* alpha, + obj_t* a, + f77_int lda, + obj_t* beta, + obj_t* c, + f77_int ldc, + num_t dt +){ + f77_char f77_uploc; + + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* betap = (float*) bli_obj_buffer( beta ); + float* cp = (float*) bli_obj_buffer( c ); + ssyrk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, (f77_int*)&lda, + betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* betap = (double*) bli_obj_buffer( beta ); + double* cp = (double*) bli_obj_buffer( c ); + dsyrk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, + (f77_int*)&lda, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* betap = (scomplex*) bli_obj_buffer( beta ); + scomplex* cp = (scomplex*) bli_obj_buffer( c ); + csyrk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, + (f77_int*)&lda, betap, cp, (f77_int*)&ldc ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* betap = (dcomplex*) bli_obj_buffer( beta ); + dcomplex* cp = (dcomplex*) bli_obj_buffer( c ); + zsyrk_( &f77_uploc, &f77_transa, &n, &k, alphap, ap, + (f77_int*)&lda, betap, cp, (f77_int*)&ldc ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_syrk( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_syrk_impl( iface, alpha, a, beta, c ); + } + else { /*CLBAS || BLAS */ + f77_int m = bli_obj_length( c ); + f77_int k = bli_obj_width_after_trans( a ); + uplo_t uploc = bli_obj_uplo( c ); + trans_t transa = bli_obj_onlytrans_status( a ); + f77_int lda, ldc; + + if( bli_obj_is_col_stored( c ) ) { + lda = bli_obj_col_stride( a ); + ldc = bli_obj_col_stride( c ); + } else { + lda = bli_obj_row_stride( a ); + ldc = bli_obj_row_stride( c ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldc = ldc + params->ld[2]; + } + + if(params->api == API_CBLAS) { + cblas_syrk(uploc, transa, m, k, alpha, a, lda, beta, c, ldc, dt); + } + else { /**/ + f77_char f77_transa; + if(bli_obj_is_col_stored( c )) { + if(transa == BLIS_TRANSPOSE) f77_transa='T'; + else if ( transa == BLIS_CONJ_TRANSPOSE ) f77_transa='N'; + else /*if ( transa == BLIS_NO_TRANSPOSE )*/ f77_transa='N'; + + blas_syrk(uploc, f77_transa, m, k, alpha, a, lda, + beta, c, ldc, dt); + } + else { + if(transa == BLIS_TRANSPOSE) f77_transa='N'; + else if ( transa == BLIS_CONJ_TRANSPOSE ) f77_transa='N'; + else /*if ( transa == BLIS_NO_TRANSPOSE )*/ f77_transa='T'; + + if( uploc == BLIS_UPPER) + uploc = BLIS_LOWER; + else if(uploc == BLIS_LOWER) + uploc = BLIS_UPPER; + + blas_syrk(uploc, f77_transa, m, k, alpha, a, lda, + beta, c, ldc, dt); + } + } + } + return ; +} + +double libblis_ref_syrk( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + num_t dt +){ + double resid = 0.0; +// double *betap = (double *)bli_obj_buffer( beta ); + + if (params->nanf) { + resid = libblis_check_nan_syrk(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_syrk_check( params, alpha, a, beta, c, c_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_isyrk_check( params, alpha, a, beta, c, c_orig); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_syrk( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_syrk_impl( iface, alpha, a, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_syrk ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +){ + num_t datatype; + dim_t m, k; + uplo_t uploc; + trans_t transa; + obj_t alpha, a, beta, c; + obj_t c_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + k = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploc ); + bli_param_map_char_to_syrk_trans( pc_str[1], &transa ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], m, k, &a ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], m, k, &aa ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &c_save ); + + // Set the structure and uplo properties of C. + bli_obj_set_struc( BLIS_SYMMETRIC, &c ); + bli_obj_set_uplo( uploc, &c ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc(alpv.real, 0.0, &alpha); + bli_setsc(betv.real, 0.0, &beta); + } + else { + // For syrk, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + bli_setsc(alpv.real, (alpv.real/0.8), &alpha); + bli_setsc(betv.real, (betv.real/1.2), &beta); + } + // Randomize A. + libblis_test_mobj_randomize( params, TRUE, &a ); + + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = (int32_t)alpv.real; + int32_t y = (int32_t)betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + // For syrk, both alpha and beta may be complex since, unlike herk, + // C is symmetric in both the real and complex cases. + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + // Randomize A. + libblis_test_mobj_irandomize( params, &a ); + + libblis_test_mobj_irandomize( params, &c ); + } + + bli_mksymm( &c ); + bli_mktrim( &c ); + + // Save C and set its structure and uplo properties. + bli_obj_set_struc( BLIS_SYMMETRIC, &c_save ); + bli_obj_set_uplo( uploc, &c_save ); + bli_copym( &c, &c_save ); + + bli_mksymm( &c_save ); + bli_mktrim( &c_save ); + + bli_copym( &a, &aa ); + + // Apply the remaining parameters. + bli_obj_set_conjtrans( transa, &a ); + + bli_obj_set_conjtrans( transa, &aa ); + + libblis_api_syrk(params, iface, &alpha, &aa, &beta, &c, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &r ); + resid = libblis_test_bitrp_syrk( params, iface, &alpha, &a, &beta, + &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_syrk(params, &alpha, &a, &beta, &c, &c_save, datatype); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_syrk_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c +) { + + switch ( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_syrk( alpha, a, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_syrk_check ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig +){ + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t k = bli_obj_width_after_trans( a ); + + obj_t at; + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized. + // - c_orig is randomized and symmetric. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transa(A)^T + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // z = ( beta * C_orig + alpha * transa(A) * transa(A)^T ) * t + // = beta * C_orig * t + alpha * transa(A) * transa(A)^T * t + // = beta * C_orig * t + alpha * transa(A) * w + // = beta * C_orig * t + z + // + + bli_obj_alias_with_trans( BLIS_TRANSPOSE, a, &at ); + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, k, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_symv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + bli_gemv( &BLIS_ONE, &at, &t, &BLIS_ZERO, &w ); + bli_gemv( alpha, a, &w, &BLIS_ZERO, &z ); + bli_symv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} diff --git a/gtestsuite/src/test_syrk.h b/gtestsuite/src/test_syrk.h new file mode 100644 index 000000000..cce2c8cf2 --- /dev/null +++ b/gtestsuite/src/test_syrk.h @@ -0,0 +1,18 @@ +#ifndef TEST_SYRK_H +#define TEST_SYRK_H + +#include "blis_test.h" + +double libblis_test_isyrk_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_syrk(obj_t* b, num_t dt ); + +#endif /* TEST_SYRK_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_trmm.cpp b/gtestsuite/src/test_trmm.cpp new file mode 100644 index 000000000..95d528003 --- /dev/null +++ b/gtestsuite/src/test_trmm.cpp @@ -0,0 +1,551 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trmm.h" + +// Local prototypes. +void libblis_test_trmm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_trmm_impl ( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b +); + +double libblis_test_trmm_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig +); + +double cblas_trmm( + f77_int mm, + f77_int nn, + f77_int lda, + f77_int ldb, + obj_t* a, + obj_t* b, + obj_t* alpha, + uplo_t uploa, + side_t side, + diag_t diaga, + trans_t transa, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_SIDE cblas_side; + enum CBLAS_DIAG cblas_diag; + enum CBLAS_TRANSPOSE cblas_transa; + + if ( bli_obj_row_stride( b ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_trans( transa ) ) + cblas_transa = CblasTrans; + else if( bli_is_conjtrans( transa ) ) + cblas_transa = CblasConjTrans; + else + cblas_transa = CblasNoTrans; + + if(bli_is_upper(uploa)) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if(bli_is_left(side)) + cblas_side = CblasLeft; + else + cblas_side = CblasRight; + + if(bli_is_unit_diag(diaga)) + cblas_diag = CblasUnit; + else + cblas_diag = CblasNonUnit; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + cblas_strmm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, *alphap, ap, lda, bp, ldb ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + cblas_dtrmm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, *alphap, ap, lda, bp, ldb ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + cblas_ctrmm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, alphap, ap, lda, bp, ldb ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + cblas_ztrmm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, alphap, ap, lda, bp, ldb ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_trmm( + f77_int mm, + f77_int nn, + f77_int lda, + f77_int ldb, + obj_t* a, + obj_t* b, + obj_t* alpha, + uplo_t uploa, + side_t side, + diag_t diaga, + trans_t transa, + num_t dt +){ + f77_char f77_side; + f77_char f77_uploa; + f77_char f77_transa; + f77_char f77_diaga; + + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + bli_param_map_blis_to_netlib_diag( diaga, &f77_diaga ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + strmm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + dtrmm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + ctrmm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + ztrmm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_trmm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + num_t dt +) { + if(params->api == API_BLIS) { + libblis_test_trmm_impl( iface, side, alpha, a, b ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_conjtrans_status( a ); + diag_t diaga = bli_obj_diag( a ); + f77_int mm = bli_obj_length( b ); + f77_int nn = bli_obj_width( b ); + f77_int lda, ldb; + + if ( bli_obj_row_stride( a ) == 1 ) + lda = bli_obj_col_stride( a ); + else + lda = bli_obj_row_stride( a ); + + if ( bli_obj_row_stride( b ) == 1 ) + ldb = bli_obj_col_stride( b ); + else + ldb = bli_obj_row_stride( b ); + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + } + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(params->api == API_CBLAS) { + cblas_trmm(mm, nn, lda, ldb, a, b, alpha, + uploa, side, diaga, transa, dt); + } else { /**/ + if( bli_obj_row_stride( a ) == 1 ) { + blas_trmm(mm, nn, lda, ldb, a, b, alpha, + uploa, side, diaga, transa, dt); + } + else { + if( side == BLIS_LEFT) + side = BLIS_RIGHT; + else if(side == BLIS_RIGHT) + side = BLIS_LEFT; + + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + blas_trmm(nn, mm, lda, ldb, a, b, alpha, + uploa, side, diaga, transa, dt); + } + } + } + return ; +} + +double libblis_ref_trmm( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + num_t dt +){ + + double resid = 0.0; + + if (params->nanf) { + resid = libblis_check_nan_trmm(b, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_trmm_check(params, side, alpha, a, b, b_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_itrmm_check(params, side, alpha, a, b, b_orig, dt); + } + else { + resid = libblis_test_matrix_check(params, b); + } + } + return resid; +} + +double libblis_test_bitrp_trmm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( b_orig, r ); + libblis_test_trmm_impl( iface, side, alpha, a, r ); + resid = libblis_test_bitrp_matrix(b, r, dt); + } + return resid; +} + +double libblis_test_op_trmm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +) { + num_t datatype; + dim_t m, n; + dim_t mn_side; + side_t side; + uplo_t uploa; + trans_t transa; + diag_t diaga; + obj_t alpha, a, b; + obj_t b_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_side( pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( pc_str[1], &uploa ); + bli_param_map_char_to_blis_diag( pc_str[3], &diaga ); + + if(params->api == API_BLIS) { + bli_param_map_char_to_blis_trans( pc_str[2], &transa ); + } else { + bli_param_map_char_to_blas_trans( pc_str[2], &transa ); + } + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + bli_set_dim_with_side( side, m, n, &mn_side ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], mn_side, mn_side, &a ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], mn_side, mn_side, &aa ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &b ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &b_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &aa ); + bli_obj_set_uplo( uploa, &aa ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &b ) ) + bli_setsc( alpv.real, 0.0, &alpha ); + else + bli_setsc( alpv.real, 0.0, &alpha ); + + // Randomize A, load the diagonal, make it densely triangular. + libblis_test_mobj_randomize( params, TRUE, &a ); + + // Randomize B and save B. + libblis_test_mobj_randomize( params, TRUE, &b ); + } + else { + int32_t x = (int32_t)alpv.real; + if ( bli_obj_is_real( &b ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + } + else { + int32_t ac = (int32_t)(x/0.8); + bli_setsc( (double)x, (double)ac, &alpha ); + } + + // Randomize A, load the diagonal, make it densely triangular + libblis_test_mobj_irandomize( params, &a ); + + // Randomize B and save B. + libblis_test_mobj_irandomize( params, &b ); + } + + if (params->nanf) { + test_fillbuffmem( &b, datatype ); + test_fillbuffmem_diag( &b, datatype ); + } + + bli_mktrim( &a ); + + //Copy b to b_save + bli_copym( &a, &aa ); + bli_copym( &b, &b_save ); + + // Apply the remaining parameters. + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + + bli_obj_set_conjtrans( transa, &aa ); + bli_obj_set_diag( diaga, &aa ); + + libblis_api_trmm(params, iface, side, &alpha, &aa, &b, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + resid = libblis_test_bitrp_trmm(params, iface, side, &alpha, &a, + &b, &b_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_trmm(params, side, &alpha, &a, &b, &b_save, datatype); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &b, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &b_save ); + + return abs(resid); +} + +void libblis_test_trmm_impl( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b +) { + switch( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_trmm( side, alpha, a, b ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_trmm_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig +) { + num_t dt = bli_obj_dt( b ); + num_t dt_real = bli_obj_dt_proj_to_real( b ); + + dim_t m = bli_obj_length( b ); + dim_t n = bli_obj_width( b ); + + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + + // Pre-conditions: + // - a is randomized and triangular. + // - b_orig is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // B := alpha * transa(A) * B_orig (side = left) + // B := alpha * B_orig * transa(A) (side = right) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = B * t + // + // z = ( alpha * transa(A) * B ) * t (side = left) + // = alpha * transa(A) * B * t + // = alpha * transa(A) * w + // + // z = ( alpha * B * transa(A) ) * t (side = right) + // = alpha * B * transa(A) * t + // = alpha * B * w + + bli_obj_scalar_init_detached( dt_real, &norm ); + + if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, m, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + else // else if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, n, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, b, &t, &BLIS_ZERO, &v ); + + if ( bli_is_left( side ) ) + { + bli_gemv( &BLIS_ONE, b_orig, &t, &BLIS_ZERO, &w ); + bli_trmv( alpha, a, &w ); + bli_copyv( &w, &z ); + } + else + { + bli_copyv( &t, &w ); + bli_trmv( &BLIS_ONE, a, &w ); + bli_gemv( alpha, b_orig, &w, &BLIS_ZERO, &z ); + } + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} diff --git a/gtestsuite/src/test_trmm.h b/gtestsuite/src/test_trmm.h new file mode 100644 index 000000000..07847fe1a --- /dev/null +++ b/gtestsuite/src/test_trmm.h @@ -0,0 +1,20 @@ +#ifndef TEST_TRMM_H +#define TEST_TRMM_H + +#include "blis_test.h" + +double libblis_test_itrmm_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + num_t dt + ); + +double libblis_check_nan_trmm(obj_t* b, num_t dt ); + + +#endif /* TEST_TRMM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_trmm3.cpp b/gtestsuite/src/test_trmm3.cpp new file mode 100644 index 000000000..81eaac964 --- /dev/null +++ b/gtestsuite/src/test_trmm3.cpp @@ -0,0 +1,358 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trmm3.h" + +using namespace std; + +// Local prototypes. +void libblis_test_trmm3_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_trmm3_impl( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +); + +double libblis_test_trmm3_check( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +); + +void libblis_api_trmm3( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +) +{ + libblis_test_trmm3_impl( iface, side, alpha, a, b, beta, c ); + return ; +} + +double libblis_ref_trmm3( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_save, + num_t dt +) { + + double resid = 0.0; + double *betap = (double *)bli_obj_buffer( beta ); + + if ((params->nanf) && (*betap == 0)) { + resid = libblis_check_nan_trmm3(c, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_trmm3_check( params, side, alpha, a, b, beta, c, c_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_itrmm3_check( params, side, alpha, a, b, beta, c, c_save ); + } + else { + resid = libblis_test_matrix_check(params, c); + } + } + return resid; +} + +double libblis_test_bitrp_trmm3( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( c_orig, r ); + libblis_test_trmm3_impl( iface, side, alpha, a, b, beta, r ); + resid = libblis_test_bitrp_matrix(c, r, dt); + } + return resid; +} + +double libblis_test_op_trmm3 ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv, + atom_t betv +) { + num_t datatype; + dim_t m, n; + dim_t mn_side; + side_t side; + uplo_t uploa; + trans_t transa, transb; + diag_t diaga; + obj_t alpha, a, b, beta, c; + obj_t c_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_side ( pc_str[0], &side ); + bli_param_map_char_to_blis_uplo ( pc_str[1], &uploa ); + bli_param_map_char_to_blis_trans( pc_str[2], &transa ); + bli_param_map_char_to_blis_diag ( pc_str[3], &diaga ); + bli_param_map_char_to_blis_trans( pc_str[4], &transb ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + bli_set_dim_with_side( side, m, n, &mn_side ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], mn_side, mn_side, &a ); + libblis_test_mobj_create( params, datatype, transb, + sc_str[2], m, n, &b ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &c_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Set alpha and beta. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &c ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + bli_setsc( betv.real, 0.0, &beta ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + bli_setsc( betv.real, (betv.real/1.2), &beta ); + } + // Randomize A, B, and C, and save C. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_mobj_randomize( params, TRUE, &b ); + libblis_test_mobj_randomize( params, TRUE, &c ); + } + else { + int32_t x = alpv.real; + int32_t y = betv.real; + if ( bli_obj_is_real( &c ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + bli_setsc( (double)y, 0.0, &beta ); + } + else { + int32_t ac = (int32_t)(x/0.8); + int32_t bc = (int32_t)(y/1.0); + bli_setsc( (double)x, (double)ac, &alpha ); + bli_setsc( (double)y, (double)bc, &beta ); + } + libblis_test_mobj_irandomize( params, &a ); + libblis_test_mobj_irandomize( params, &b ); + libblis_test_mobj_irandomize( params, &c ); + } + + if ((params->nanf) && (betv.real == 0) ) { + test_fillbuffmem(&c, datatype ); + } + + // Randomize A, make it densely triangular. + bli_mktrim( &a ); + + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + bli_obj_set_conjtrans( transb, &b ); + + //Copy c to c_save + bli_copym( &c, &c_save ); + + libblis_api_trmm3(params, iface, side, &alpha, &a, &b, &beta, &c ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + resid = libblis_test_bitrp_trmm3( params, iface, side,&alpha, &a, &b, + &beta, &c, &c_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_trmm3(params, side, &alpha, &a, &b, &beta, + &c, &c_save, datatype ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &c, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &c ); + libblis_test_obj_free( &c_save ); + + return abs(resid); +} + +void libblis_test_trmm3_impl ( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c +) +{ + switch( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_trmm3( side, alpha, a, b, beta, c ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_trmm3_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig +) +{ + num_t dt = bli_obj_dt( c ); + num_t dt_real = bli_obj_dt_proj_to_real( c ); + + dim_t m = bli_obj_length( c ); + dim_t n = bli_obj_width( c ); + + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + + // Pre-conditions: + // - a is randomized and triangular. + // - b is randomized. + // - c_orig is randomized. + // Note: + // - alpha and beta should have non-zero imaginary components in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // C := beta * C_orig + alpha * transa(A) * transb(B) (side = left) + // C := beta * C_orig + alpha * transb(B) * transa(A) (side = right) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = C * t + // + // z = ( beta * C_orig + alpha * transa(A) * transb(B) ) * t (side = left) + // = beta * C_orig * t + alpha * transa(A) * transb(B) * t + // = beta * C_orig * t + alpha * transa(A) * w + // = beta * C_orig * t + z + // + // z = ( beta * C_orig + alpha * transb(B) * transa(A) ) * t (side = right) + // = beta * C_orig * t + alpha * transb(B) * transa(A) * t + // = beta * C_orig * t + alpha * transb(B) * w + // = beta * C_orig * t + z + + bli_obj_scalar_init_detached( dt_real, &norm ); + + if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, m, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + else // else if ( bli_is_left( side ) ) + { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, n, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, c, &t, &BLIS_ZERO, &v ); + + if ( bli_is_left( side ) ) + { + bli_gemv( &BLIS_ONE, b, &t, &BLIS_ZERO, &w ); + bli_trmv( alpha, a, &w ); + bli_copyv( &w, &z ); + } + else + { + bli_copyv( &t, &w ); + bli_trmv( &BLIS_ONE, a, &w ); + bli_gemv( alpha, b, &w, &BLIS_ZERO, &z ); + } + + bli_gemv( beta, c_orig, &t, &BLIS_ONE, &z ); + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_trmm3.h b/gtestsuite/src/test_trmm3.h new file mode 100644 index 000000000..1467586fb --- /dev/null +++ b/gtestsuite/src/test_trmm3.h @@ -0,0 +1,20 @@ +#ifndef TEST_TRMM3_H +#define TEST_TRMM3_H + +#include "blis_test.h" + +double libblis_test_itrmm3_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* beta, + obj_t* c, + obj_t* c_orig + ); + +double libblis_check_nan_trmm3(obj_t* c, num_t dt ); + +#endif /* TEST_TRMM3_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_trmv.cpp b/gtestsuite/src/test_trmv.cpp new file mode 100644 index 000000000..0f5a518ae --- /dev/null +++ b/gtestsuite/src/test_trmv.cpp @@ -0,0 +1,500 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trmv.h" + +// Local prototypes. +void libblis_test_trmv_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_trmv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x +); + +double libblis_test_trmv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +); + +void libblis_trmv_alphax( + obj_t* alpha, + f77_int m, + obj_t* x, + f77_int incx, + num_t dt +){ + int i, ix = 0; + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* xp = (float*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = (*Alpha * xp[ix]); + ix = ix + incx; + } + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* xp = (double*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = (*Alpha * xp[ix]); + ix = ix + incx; + } + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = mulc(*Alpha , xp[ix]); + ix = ix + incx; + } + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = mulc(*Alpha , xp[ix]); + ix = ix + incx; + } + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void cblas_trmv( + uplo_t uploa, + trans_t transa, + diag_t diaga, + f77_int m, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + num_t dt +){ + enum CBLAS_ORDER cblas_order = CblasColMajor; + enum CBLAS_UPLO cblas_uploa; + enum CBLAS_DIAG cblas_diaga; + enum CBLAS_TRANSPOSE cblas_transa; + + if(bli_is_upper(uploa)) + cblas_uploa = CblasUpper; + else + cblas_uploa = CblasLower; + + if( bli_is_trans( transa ) ) + cblas_transa = CblasTrans; + else if( bli_is_conjtrans( transa ) ) + cblas_transa = CblasConjTrans; + else + cblas_transa = CblasNoTrans; + + if(bli_is_unit_diag(diaga)) + cblas_diaga = CblasUnit; + else + cblas_diaga = CblasNonUnit; + + switch( dt ) { + case BLIS_FLOAT : + { + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + cblas_strmv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + case BLIS_DOUBLE : + { + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + cblas_dtrmv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cblas_ctrmv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + cblas_ztrmv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_trmv( + uplo_t uploa, + trans_t transa, + diag_t diaga, + f77_int m, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + num_t dt +){ + f77_char f77_uploa; + f77_char f77_transa; + f77_char f77_diaga; + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + bli_param_map_blis_to_netlib_diag( diaga, &f77_diaga ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + strmv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + case BLIS_DOUBLE : + { + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + dtrmv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + ctrmv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + ztrmv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_trmv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_trmv_impl( iface, alpha, a, x ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_conjtrans_status( a ); + diag_t diaga = bli_obj_diag( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + libblis_trmv_alphax(alpha, mm, x, incx, dt); + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(params->api == API_CBLAS) { + cblas_trmv(uploa, transa, diaga, mm, a, lda, x, incx, dt ); + } + else { /**/ + if ( bli_obj_row_stride( a ) == 1 ){ + blas_trmv(uploa, transa, diaga, mm, a, lda, x, incx, dt ); + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + blas_trmv(uploa, transa, diaga, mm, a, lda, x, incx, dt ); + } + } + } + return ; +} + +double libblis_ref_trmv( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_trmv_check( params, alpha, a, x, x_orig ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_itrmv_check( params, alpha, a, x, x_orig ); + } + else { + resid = libblis_test_vector_check(params, x); + } + } + return resid; +} + +double libblis_test_bitrp_trmv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( x_orig, r ); + libblis_test_trmv_impl( iface, alpha, a, r); + resid = libblis_test_bitrp_vector(x, r, dt); + } + return resid; +} + +double libblis_test_op_trmv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + trans_t transa; + diag_t diaga; + obj_t alpha, a, x; + obj_t x_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_trans( pc_str[1], &transa ); + bli_param_map_char_to_blis_diag( pc_str[2], &diaga ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &aa ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &x_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &aa ); + bli_obj_set_uplo( uploa, &aa ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &x ) ) + bli_setsc( -1.0, 0.0, &alpha ); + else + bli_setsc( -0.5, 0.5, &alpha ); + + // Randomize A, make it densely triangular. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_vobj_randomize( params, TRUE, &x ); + } + else{ + if ( bli_obj_is_real( &x ) ) + bli_setsc( -1.0, 0.0, &alpha ); + else + bli_setsc( 1.0, -1.5, &alpha ); + + // Randomize A, make it densely triangular. + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &x ); + } + + bli_mktrim( &a ); + bli_copyv( &x, &x_save ); + + bli_copym( &a, &aa ); + + // Apply the remaining parameters. + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + + bli_obj_set_conjtrans( transa, &aa ); + bli_obj_set_diag( diaga, &aa ); + + libblis_api_trmv(params, iface, &alpha, &aa, &x, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_trmv( params, iface, &alpha, &a, &x, + &x_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_trmv( params, &alpha, &a, &x, &x_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &x_save ); + + return abs(resid); +} + +void libblis_test_trmv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_trmv( alpha, a, x ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_trmv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +){ + num_t dt = bli_obj_dt( x ); + num_t dt_real = bli_obj_dt_proj_to_real( x ); + + dim_t m = bli_obj_vector_dim( x ); + + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_conjtrans_status( a ); + + obj_t a_local, y; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized and triangular. + // - x is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // x := alpha * transa(A) * x_orig + // + // is functioning correctly if + // + // normfv( y - x ) + // + // is negligible, where + // + // y = alpha * conja(A_dense) * x_orig + // + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &y ); + bli_obj_create( dt, m, m, 0, 0, &a_local ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &a_local ); + bli_obj_set_uplo( uploa, &a_local ); + bli_obj_toggle_uplo_if_trans( transa, &a_local ); + bli_copym( a, &a_local ); + bli_mktrim( &a_local ); + + bli_obj_set_struc( BLIS_GENERAL, &a_local ); + bli_obj_set_uplo( BLIS_DENSE, &a_local ); + + bli_gemv( alpha, &a_local, x_orig, &BLIS_ZERO, &y ); + + bli_subv( x, &y ); + bli_normfv( &y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &y ); + bli_obj_free( &a_local ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_trmv.h b/gtestsuite/src/test_trmv.h new file mode 100644 index 000000000..9319880ac --- /dev/null +++ b/gtestsuite/src/test_trmv.h @@ -0,0 +1,17 @@ +#ifndef TEST_TRMV_H +#define TEST_TRMV_H + +#include "blis_test.h" + +double libblis_test_itrmv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig + ); + +double libblis_check_nan_trmv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_TRMV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_trsm.cpp b/gtestsuite/src/test_trsm.cpp new file mode 100644 index 000000000..83ba4f768 --- /dev/null +++ b/gtestsuite/src/test_trsm.cpp @@ -0,0 +1,549 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trsm.h" + +// Local prototypes. +void libblis_test_trsm_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_trsm_impl ( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b +); + +double libblis_test_trsm_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig +); + +double cblas_trsm( + f77_int mm, + f77_int nn, + f77_int lda, + f77_int ldb, + obj_t* a, + obj_t* b, + obj_t* alpha, + uplo_t uploa, + side_t side, + diag_t diaga, + trans_t transa, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uplo; + enum CBLAS_SIDE cblas_side; + enum CBLAS_DIAG cblas_diag; + enum CBLAS_TRANSPOSE cblas_transa; + + if ( bli_obj_row_stride( b ) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if( bli_is_trans( transa ) ) + cblas_transa = CblasTrans; + else if( bli_is_conjtrans( transa ) ) + cblas_transa = CblasConjTrans; + else + cblas_transa = CblasNoTrans; + + if(bli_is_upper(uploa)) + cblas_uplo = CblasUpper; + else + cblas_uplo = CblasLower; + + if(bli_is_left(side)) + cblas_side = CblasLeft; + else + cblas_side = CblasRight; + + if(bli_is_unit_diag(diaga)) + cblas_diag = CblasUnit; + else + cblas_diag = CblasNonUnit; + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + cblas_strsm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, *alphap, ap, lda, bp, ldb ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + cblas_dtrsm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, *alphap, ap, lda, bp, ldb ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + cblas_ctrsm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, alphap, ap, lda, bp, ldb ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + cblas_ztrsm( cblas_order, cblas_side, cblas_uplo, cblas_transa, + cblas_diag, mm, nn, alphap, ap, lda, bp, ldb ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +double blas_trsm( + f77_int mm, + f77_int nn, + f77_int lda, + f77_int ldb, + obj_t* a, + obj_t* b, + obj_t* alpha, + uplo_t uploa, + side_t side, + diag_t diaga, + trans_t transa, + num_t dt +){ + f77_char f77_side; + f77_char f77_uploa; + f77_char f77_transa; + f77_char f77_diaga; + + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + bli_param_map_blis_to_netlib_diag( diaga, &f77_diaga ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* alphap = (float*) bli_obj_buffer( alpha ); + float* ap = (float*) bli_obj_buffer( a ); + float* bp = (float*) bli_obj_buffer( b ); + strsm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + case BLIS_DOUBLE : + { + double* alphap = (double*) bli_obj_buffer( alpha ); + double* ap = (double*) bli_obj_buffer( a ); + double* bp = (double*) bli_obj_buffer( b ); + dtrsm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* alphap = (scomplex*) bli_obj_buffer( alpha ); + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* bp = (scomplex*) bli_obj_buffer( b ); + ctrsm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* alphap = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* bp = (dcomplex*) bli_obj_buffer( b ); + ztrsm_( &f77_side, &f77_uploa, &f77_transa, &f77_diaga, + &mm, &nn, alphap, ap, &lda, bp, &ldb ); + break; + } + default : + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } + return 0; +} + +void libblis_api_trsm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + num_t dt +) { + if(params->api == API_BLIS) { + libblis_test_trsm_impl( iface, side, alpha, a, b ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_conjtrans_status( a ); + diag_t diaga = bli_obj_diag( a ); + f77_int mm = bli_obj_length( b ); + f77_int nn = bli_obj_width( b ); + f77_int lda, ldb; + + if ( bli_obj_row_stride( a ) == 1 ) + lda = bli_obj_col_stride( a ); + else + lda = bli_obj_row_stride( a ); + + if ( bli_obj_row_stride( b ) == 1 ) + ldb = bli_obj_col_stride( b ); + else + ldb = bli_obj_row_stride( b ); + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + ldb = ldb + params->ld[1]; + } + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(params->api == API_CBLAS) { + cblas_trsm(mm, nn, lda, ldb, a, b, alpha, + uploa, side, diaga, transa, dt); + } else { /**/ + if( bli_obj_row_stride( a ) == 1 ) { + blas_trsm(mm, nn, lda, ldb, a, b, alpha, + uploa, side, diaga, transa, dt); + } + else { + if( side == BLIS_LEFT) + side = BLIS_RIGHT; + else if(side == BLIS_RIGHT) + side = BLIS_LEFT; + + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + + blas_trsm(nn, mm, lda, ldb, a, b, alpha, + uploa, side, diaga, transa, dt); + } + } + } + return ; +} + +double libblis_ref_trsm( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + num_t dt +){ + + double resid = 0.0; + + if (params->nanf) { + resid = libblis_check_nan_trsm(b, dt ); + } + else if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_trsm_check(params, side, alpha, a, b, b_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_itrsm_check(params, side, alpha, a, b, b_orig, dt); + } + else { + resid = libblis_test_matrix_check(params, b); + } + } + return resid; +} + +double libblis_test_bitrp_trsm( + test_params_t* params, + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( b_orig, r ); + libblis_test_trsm_impl( iface, side, alpha, a, r ); + resid = libblis_test_bitrp_matrix(b, r, dt); + } + return resid; +} + +double libblis_test_op_trsm ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +) { + num_t datatype; + dim_t m, n; + dim_t mn_side; + side_t side; + uplo_t uploa; + trans_t transa; + diag_t diaga; + obj_t alpha, a, b; + obj_t b_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_side( pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( pc_str[1], &uploa ); + bli_param_map_char_to_blis_diag( pc_str[3], &diaga ); + + if(params->api == API_BLIS) { + bli_param_map_char_to_blis_trans( pc_str[2], &transa ); + } else { + bli_param_map_char_to_blas_trans( pc_str[2], &transa ); + } + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + bli_set_dim_with_side( side, m, n, &mn_side ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], mn_side, mn_side, &a ); + libblis_test_mobj_create( params, datatype, transa, + sc_str[1], mn_side, mn_side, &aa ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &b ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &b_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &aa ); + bli_obj_set_uplo( uploa, &aa ); + + // Set alpha. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &b ) ) + bli_setsc( alpv.real, 0.0, &alpha ); + else + bli_setsc( alpv.real, 0.0, &alpha ); + + // Randomize A, load the diagonal, make it densely triangular. + libblis_test_mobj_randomize( params, TRUE, &a ); + + // Randomize B and save B. + libblis_test_mobj_randomize( params, TRUE, &b ); + } + else { + int32_t x = (int32_t)alpv.real; + if ( bli_obj_is_real( &b ) ) { + bli_setsc( (double)x, 0.0, &alpha ); + } + else { + int32_t ac = (int32_t)(x/0.8); + bli_setsc( (double)x, (double)ac, &alpha ); + } + + // Randomize A, load the diagonal, make it densely triangular. + libblis_test_mobj_irandomize( params, &a ); + + // Randomize B and save B. + libblis_test_mobj_irandomize( params, &b ); + } + + if (params->nanf) { + test_fillbuffmem( &b, datatype ); + test_fillbuffmem_diag( &b, datatype ); + } + + libblis_test_mobj_load_diag( params, &a ); + bli_mktrim( &a ); + + //Copy b to b_save + bli_copym( &a, &aa ); + bli_copym( &b, &b_save ); + + // Apply the remaining parameters. + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + + bli_obj_set_conjtrans( transa, &aa ); + bli_obj_set_diag( diaga, &aa ); + + libblis_api_trsm(params, iface, side, &alpha, &aa, &b, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + resid = libblis_test_bitrp_trsm(params, iface, side, &alpha, &a, + &b, &b_save, &r, datatype); + bli_obj_free( &r ); + } + else { + resid = libblis_ref_trsm(params, side, &alpha, &a, &b, &b_save, datatype); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &b, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &b ); + libblis_test_obj_free( &b_save ); + + return resid; +} + +void libblis_test_trsm_impl( + iface_t iface, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b +) +{ + switch( iface ) { + case BLIS_TEST_SEQ_FRONT_END: + bli_trsm( side, alpha, a, b ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_trsm_check ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig +) { + num_t dt = bli_obj_dt( b ); + num_t dt_real = bli_obj_dt_proj_to_real( b ); + + dim_t m = bli_obj_length( b ); + dim_t n = bli_obj_width( b ); + + obj_t norm; + obj_t t, v, w, z; + + double junk; + double resid = 0.0; + // + // Pre-conditions: + // - a is randomized and triangular. + // - b_orig is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // B := alpha * inv(transa(A)) * B_orig (side = left) + // B := alpha * B_orig * inv(transa(A)) (side = right) + // + // is functioning correctly if + // + // normfv( v - z ) + // + // is negligible, where + // + // v = B * t + // + // z = ( alpha * inv(transa(A)) * B ) * t (side = left) + // = alpha * inv(transa(A)) * B * t + // = alpha * inv(transa(A)) * w + // + // z = ( alpha * B * inv(transa(A)) ) * t (side = right) + // = alpha * B * tinv(ransa(A)) * t + // = alpha * B * w + + bli_obj_scalar_init_detached( dt_real, &norm ); + + if ( bli_is_left( side ) ) { + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, m, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + else { // else if ( bli_is_left( side ) ) + bli_obj_create( dt, n, 1, 0, 0, &t ); + bli_obj_create( dt, m, 1, 0, 0, &v ); + bli_obj_create( dt, n, 1, 0, 0, &w ); + bli_obj_create( dt, m, 1, 0, 0, &z ); + } + + libblis_test_vobj_randomize( params, TRUE, &t ); + + bli_gemv( &BLIS_ONE, b, &t, &BLIS_ZERO, &v ); + + if ( bli_is_left( side ) ) { + bli_gemv( alpha, b_orig, &t, &BLIS_ZERO, &w ); + bli_trsv( &BLIS_ONE, a, &w ); + bli_copyv( &w, &z ); + } + else { + bli_copyv( &t, &w ); + bli_trsv( &BLIS_ONE, a, &w ); + bli_gemv( alpha, b_orig, &w, &BLIS_ZERO, &z ); + } + + bli_subv( &z, &v ); + bli_normfv( &v, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &t ); + bli_obj_free( &v ); + bli_obj_free( &w ); + bli_obj_free( &z ); + + return resid; +} diff --git a/gtestsuite/src/test_trsm.h b/gtestsuite/src/test_trsm.h new file mode 100644 index 000000000..5da828a0d --- /dev/null +++ b/gtestsuite/src/test_trsm.h @@ -0,0 +1,20 @@ +#ifndef TEST_TRSM_H +#define TEST_TRSM_H + +#include "blis_test.h" + +double libblis_test_itrsm_check + ( + test_params_t* params, + side_t side, + obj_t* alpha, + obj_t* a, + obj_t* b, + obj_t* b_orig, + num_t dt + ); + +double libblis_check_nan_trsm(obj_t* b, num_t dt ); + + +#endif /* TEST_TRSM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_trsv.cpp b/gtestsuite/src/test_trsv.cpp new file mode 100644 index 000000000..aea316038 --- /dev/null +++ b/gtestsuite/src/test_trsv.cpp @@ -0,0 +1,529 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_trsv.h" + +// Local prototypes. +void libblis_test_trsv_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_trsv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x +); + +double libblis_test_trsv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +); + +void libblis_alphax( + obj_t* alpha, + f77_int m, + obj_t* x, + f77_int incx, + num_t dt +){ + int i, ix = 0; + switch( dt ) { + case BLIS_FLOAT : + { + float* Alpha = (float*) bli_obj_buffer( alpha ); + float* xp = (float*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = (*Alpha * xp[ix]); + ix = ix + incx; + } + break; + } + case BLIS_DOUBLE : + { + double* Alpha = (double*) bli_obj_buffer( alpha ); + double* xp = (double*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = (*Alpha * xp[ix]); + ix = ix + incx; + } + break; + } + case BLIS_SCOMPLEX : + { + scomplex* Alpha = (scomplex*) bli_obj_buffer( alpha ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = mulc(*Alpha , xp[ix]); + ix = ix + incx; + } + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* Alpha = (dcomplex*) bli_obj_buffer( alpha ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + for(i = 0 ; i < m ; i++) { + xp[ix] = mulc(*Alpha , xp[ix]); + ix = ix + incx; + } + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void cblas_trsv( + uplo_t uploa, + trans_t transa, + diag_t diaga, + f77_int m, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + num_t dt +){ + enum CBLAS_ORDER cblas_order; + enum CBLAS_UPLO cblas_uploa; + enum CBLAS_DIAG cblas_diaga; + enum CBLAS_TRANSPOSE cblas_transa; + + if(bli_obj_row_stride(a) == 1 ) + cblas_order = CblasColMajor; + else + cblas_order = CblasRowMajor; + + if(bli_is_upper(uploa)) + cblas_uploa = CblasUpper; + else + cblas_uploa = CblasLower; + + if( bli_is_trans( transa ) ) + cblas_transa = CblasTrans; + else if( bli_is_conjtrans( transa ) ) + cblas_transa = CblasConjTrans; + else + cblas_transa = CblasNoTrans; + + if(bli_is_unit_diag(diaga)) + cblas_diaga = CblasUnit; + else + cblas_diaga = CblasNonUnit; + + switch( dt ) { + case BLIS_FLOAT : + { + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + cblas_strsv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + case BLIS_DOUBLE : + { + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + cblas_dtrsv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + cblas_ctrsv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + cblas_ztrsv(cblas_order, cblas_uploa, cblas_transa, cblas_diaga, + m, ap, lda, xp, incx ); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void blas_trsv( + uplo_t uploa, + trans_t transa, + diag_t diaga, + f77_int m, + obj_t* a, + f77_int lda, + obj_t* x, + f77_int incx, + num_t dt +){ + f77_char f77_uploa; + f77_char f77_transa; + f77_char f77_diaga; + trans_t trans; + + if( bli_obj_row_stride( a ) == 1 ){ + if ( transa == BLIS_TRANSPOSE ) trans = BLIS_TRANSPOSE; + else if ( transa == BLIS_CONJ_TRANSPOSE ) trans = BLIS_CONJ_TRANSPOSE; + else /*if(transa == BLIS_NO_TRANSPOSE)*/ trans = BLIS_NO_TRANSPOSE; + } + else { + if( uploa == BLIS_UPPER) + uploa = BLIS_LOWER; + else if(uploa == BLIS_LOWER) + uploa = BLIS_UPPER; + if(transa == BLIS_NO_TRANSPOSE) trans = BLIS_TRANSPOSE; + else if(transa == BLIS_TRANSPOSE) trans = BLIS_NO_TRANSPOSE; + else /*if ( transa == BLIS_CONJ_TRANSPOSE)*/ trans = BLIS_NO_TRANSPOSE; + } + + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + bli_param_map_blis_to_netlib_trans( trans, &f77_transa ); + bli_param_map_blis_to_netlib_diag( diaga, &f77_diaga ); + + switch( dt ) { + case BLIS_FLOAT : + { + float* ap = (float*) bli_obj_buffer( a ); + float* xp = (float*) bli_obj_buffer( x ); + strsv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + case BLIS_DOUBLE : + { + double* ap = (double*) bli_obj_buffer( a ); + double* xp = (double*) bli_obj_buffer( x ); + dtrsv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + case BLIS_SCOMPLEX : + { + scomplex* ap = (scomplex*) bli_obj_buffer( a ); + scomplex* xp = (scomplex*) bli_obj_buffer( x ); + ctrsv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + case BLIS_DCOMPLEX : + { + dcomplex* ap = (dcomplex*) bli_obj_buffer( a ); + dcomplex* xp = (dcomplex*) bli_obj_buffer( x ); + ztrsv_(&f77_uploa, &f77_transa, &f77_diaga, &m, ap, (f77_int*)&lda, xp, &incx); + break; + } + default: + bli_check_error_code( BLIS_INVALID_DATATYPE ); + } +} + +void libblis_api_trsv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + num_t dt +){ + + if(params->api == API_BLIS) { + libblis_test_trsv_impl( iface, alpha, a, x ); + } + else { /*CLBAS || BLAS */ + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_conjtrans_status( a ); + diag_t diaga = bli_obj_diag( a ); + f77_int mm = bli_obj_length( a ); + f77_int incx = bli_obj_vector_inc( x ); + f77_int lda ; + + if ( bli_obj_row_stride( a ) == 1 ) { + lda = bli_obj_col_stride( a ); + } else { + lda = bli_obj_row_stride( a ); + } + + if(params->ldf == 1) { + lda = lda + params->ld[0]; + } + + libblis_alphax(alpha, mm, x, incx, dt); + + if(bli_obj_has_notrans(a) && bli_obj_has_conj(a)) { + conjugate_tensor(a, dt); + transa = bli_obj_onlytrans_status( a ); + } + + if(params->api == API_CBLAS) { + cblas_trsv(uploa, transa, diaga, mm, a, lda, x, incx, dt ); + } + else { /**/ + if ( bli_obj_row_stride( a ) == 1 ){ + blas_trsv(uploa, transa, diaga, mm, a, lda, x, incx, dt ); + } + else { + blas_trsv(uploa, transa, diaga, mm, a, lda, x, incx, dt ); + } + } + } + return ; +} + +double libblis_ref_trsv( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + resid = libblis_test_trsv_check( params, alpha, a, x, x_orig); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_itrsv_check( params, alpha, a, x, x_orig); + } + else { + resid = libblis_test_vector_check(params, x); + } + } + return resid; +} + +double libblis_test_bitrp_trsv( + test_params_t* params, + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( x_orig, r ); + libblis_test_trsv_impl( iface, alpha, a, x ); + resid = libblis_test_bitrp_vector(x, r, dt); + } + return resid; +} + +double libblis_test_op_trsv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpv +){ + num_t datatype; + dim_t m; + uplo_t uploa; + trans_t transa; + diag_t diaga; + obj_t alpha, a, x; + obj_t x_save; + double resid = 0.0; + obj_t aa; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to an actual dimension. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_uplo( pc_str[0], &uploa ); + bli_param_map_char_to_blis_trans( pc_str[1], &transa ); + bli_param_map_char_to_blis_diag( pc_str[2], &diaga ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &alpha ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &a ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, m, &aa ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &x ); + libblis_test_vobj_create( params, datatype, + sc_str[1], m, &x_save ); + + // Set the structure and uplo properties of A. + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &aa ); + bli_obj_set_uplo( uploa, &aa ); + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + if ( bli_obj_is_real( &x ) ) { + bli_setsc( alpv.real, 0.0, &alpha ); + } + else { + bli_setsc( alpv.real, (alpv.real/0.8), &alpha ); + } + // Randomize A, make it densely triangular. + libblis_test_mobj_randomize( params, TRUE, &a ); + libblis_test_vobj_randomize( params, TRUE, &x ); + } + else{ + int32_t xx = (int32_t)alpv.real; + if ( bli_obj_is_real( &x ) ) { + bli_setsc( (double)xx, 0.0, &alpha ); + } + else { + int32_t ac = (int32_t)(xx/0.8); + bli_setsc( (double)xx, (double)ac, &alpha ); + } + // Randomize A, make it densely triangular. + libblis_test_mobj_irandomize( params, &a ); + libblis_test_vobj_irandomize( params, &x ); + } + + // Randomize A, load the diagonal, make it densely triangular. + libblis_test_mobj_load_diag( params, &a ); + bli_mktrim( &a ); + bli_copyv( &x, &x_save ); + + bli_copym( &a, &aa ); + + // Apply the remaining parameters. + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + + bli_obj_set_conjtrans( transa, &aa ); + bli_obj_set_diag( diaga, &aa ); + + libblis_api_trsv(params, iface, &alpha, &aa, &x, datatype); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_trsv( params, iface, &alpha, &a, &x, + &x_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_trsv( params, &alpha, &a, &x, &x_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &x, &resid ); + + // Free the test objects. + libblis_test_obj_free( &a ); + libblis_test_obj_free( &aa ); + libblis_test_obj_free( &x ); + libblis_test_obj_free( &x_save ); + + return resid; +} + +void libblis_test_trsv_impl( + iface_t iface, + obj_t* alpha, + obj_t* a, + obj_t* x +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_trsv( alpha, a, x ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_trsv_check( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig +){ + num_t dt = bli_obj_dt( x ); + num_t dt_real = bli_obj_dt_proj_to_real( x ); + + dim_t m = bli_obj_vector_dim( x ); + + uplo_t uploa = bli_obj_uplo( a ); + trans_t transa = bli_obj_conjtrans_status( a ); + + obj_t alpha_inv; + obj_t a_local, y; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - a is randomized and triangular. + // - x is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the + // complex cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // x := alpha * inv(transa(A)) * x_orig + // + // is functioning correctly if + // + // normfv( y - x_orig ) + // + // is negligible, where + // + // y = inv(alpha) * transa(A_dense) * x + // + + bli_obj_scalar_init_detached( dt, &alpha_inv ); + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_copysc( &BLIS_ONE, &alpha_inv ); + bli_divsc( alpha, &alpha_inv ); + + bli_obj_create( dt, m, 1, 0, 0, &y ); + bli_obj_create( dt, m, m, 0, 0, &a_local ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &a_local ); + bli_obj_set_uplo( uploa, &a_local ); + bli_obj_toggle_uplo_if_trans( transa, &a_local ); + bli_copym( a, &a_local ); + bli_mktrim( &a_local ); + + bli_obj_set_struc( BLIS_GENERAL, &a_local ); + bli_obj_set_uplo( BLIS_DENSE, &a_local ); + + bli_gemv( &alpha_inv, &a_local, x, &BLIS_ZERO, &y ); + + bli_subv( x_orig, &y ); + bli_normfv( &y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &y ); + bli_obj_free( &a_local ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_trsv.h b/gtestsuite/src/test_trsv.h new file mode 100644 index 000000000..8e8a4b765 --- /dev/null +++ b/gtestsuite/src/test_trsv.h @@ -0,0 +1,17 @@ +#ifndef TEST_TRSV_H +#define TEST_TRSV_H + +#include "blis_test.h" + +double libblis_test_itrsv_check + ( + test_params_t* params, + obj_t* alpha, + obj_t* a, + obj_t* x, + obj_t* x_orig + ); + +double libblis_check_nan_trsv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_TRMV_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_xpbym.cpp b/gtestsuite/src/test_xpbym.cpp new file mode 100644 index 000000000..fd2b939cc --- /dev/null +++ b/gtestsuite/src/test_xpbym.cpp @@ -0,0 +1,227 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_xpbym.h" + +// Local prototypes. +void libblis_test_xpbym_deps( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_xpbym_impl( + iface_t iface, + obj_t* x, + obj_t* beta, + obj_t* y +); + +double libblis_test_xpbym_check( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_save +); + +double libblis_ref_xpbym( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_xpbym_check( params, x, beta, y, y_orig ); + } + else { + resid = libblis_test_ixpbym_check( params, x, beta, y, y_orig ); + } + return resid; +} + +double libblis_test_bitrp_xpbym( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copym( y_orig, r ); + libblis_test_xpbym_impl( iface, x, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_xpbym ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha +){ + num_t datatype; + dim_t m, n; + trans_t transx; + obj_t x, beta, y; + obj_t y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + n = dim->n; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_trans( pc_str[0], &transx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_mobj_create( params, datatype, transx, + sc_str[0], m, n, &x ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y ); + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &y_save ); + + // Set beta. + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_mobj_randomize( params, FALSE, &x ); + libblis_test_mobj_randomize( params, FALSE, &y ); + } else { + libblis_test_mobj_irandomize( params, &x ); + libblis_test_mobj_irandomize( params, &y ); + } + bli_copym( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conjtrans( transx, &x ); + + libblis_test_xpbym_impl( iface, &x, &beta, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_mobj_create( params, datatype, BLIS_NO_TRANSPOSE, + sc_str[0], m, n, &r ); + + resid = libblis_test_bitrp_xpbym(params, iface, &x, &beta, &y, + &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_xpbym( params, &x, &beta, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output matrix is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_xpbym_impl( + iface_t iface, + obj_t* x, + obj_t* beta, + obj_t* y +){ + switch ( iface ){ + case BLIS_TEST_SEQ_FRONT_END: + bli_xpbym( x, beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_xpbym_check( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +){ + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_length( y ); + dim_t n = bli_obj_width( y ); + + obj_t x_temp, y_temp; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y_orig is randomized. + // Note: + // - alpha should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + conjx(x) + // + // is functioning correctly if + // + // normfm( y - ( beta * y_orig + conjx(x) ) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, n, 0, 0, &x_temp ); + bli_obj_create( dt, m, n, 0, 0, &y_temp ); + + bli_copym( x, &x_temp ); + bli_copym( y_orig, &y_temp ); + + bli_scalm( beta, &y_temp ); + bli_addm( &x_temp, &y_temp ); + + bli_subm( &y_temp, y ); + bli_normfm( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_xpbym.h b/gtestsuite/src/test_xpbym.h new file mode 100644 index 000000000..76301d978 --- /dev/null +++ b/gtestsuite/src/test_xpbym.h @@ -0,0 +1,17 @@ +#ifndef TEST_XPBYM_H +#define TEST_XPBYM_H + +#include "blis_test.h" + +double libblis_test_ixpbym_check + ( + test_params_t* params, + obj_t* x, + obj_t* alpha, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_xpbym( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_XPBYM_H */ \ No newline at end of file diff --git a/gtestsuite/src/test_xpbyv.cpp b/gtestsuite/src/test_xpbyv.cpp new file mode 100644 index 000000000..13fd5d9bb --- /dev/null +++ b/gtestsuite/src/test_xpbyv.cpp @@ -0,0 +1,228 @@ +#include "blis_test.h" +#include "blis_utils.h" +#include "test_xpbyv.h" + +// Local prototypes. +void libblis_test_xpbyv_deps ( + thread_data_t* tdata, + test_params_t* params, + test_op_t* op +); + +void libblis_test_xpbyv_impl ( + iface_t iface, + obj_t* x, + obj_t* beta, + obj_t* y +); + +double libblis_test_xpbyv_check ( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +); + +double libblis_ref_xpbyv( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_save +) { + double resid = 0.0; + + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + // Perform checks. + resid = libblis_test_xpbyv_check( params, x, beta, y, y_save ); + } + else { + if(params->oruflw == BLIS_DEFAULT) { + resid = libblis_test_ixpbyv_check( params, x, beta, y, y_save ); + } + else { + resid = libblis_test_vector_check(params, x); + } + } + return resid; +} + +double libblis_test_bitrp_xpbyv( + test_params_t* params, + iface_t iface, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig, + obj_t* r, + num_t dt +) { + double resid = 0.0; + unsigned int n_repeats = params->n_repeats; + unsigned int i; + + for(i = 0; i < n_repeats; i++) { + bli_copyv( y_orig, r ); + libblis_test_xpbyv_impl( iface, x, beta, r ); + resid = libblis_test_bitrp_vector(y, r, dt); + } + return resid; +} + +double libblis_test_op_xpbyv ( + test_params_t* params, + iface_t iface, + char* dc_str, + char* pc_str, + char* sc_str, + tensor_t* dim, + atom_t alpha +){ + num_t datatype; + dim_t m; + conj_t conjx; + obj_t beta, x, y; + obj_t y_save; + double resid = 0.0; + + // Use the datatype of the first char in the datatype combination string. + bli_param_map_char_to_blis_dt( dc_str[0], &datatype ); + + // Map the dimension specifier to actual dimensions. + m = dim->m; + + // Map parameter characters to BLIS constants. + bli_param_map_char_to_blis_conj( pc_str[0], &conjx ); + + // Create test scalars. + bli_obj_scalar_init_detached( datatype, &beta ); + + // Create test operands (vectors and/or matrices). + libblis_test_vobj_create( params, datatype, sc_str[0], m, &x ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y ); + libblis_test_vobj_create( params, datatype, sc_str[1], m, &y_save ); + + // Set beta. + if ( bli_obj_is_real( &y ) ) + bli_setsc( -2.0, 0.0, &beta ); + else + bli_setsc( 0.0, -2.0, &beta ); + + // Randomize x and y, and save y. + if((params->bitextf == 0) && (params->oruflw == BLIS_DEFAULT)) { + libblis_test_vobj_randomize( params, FALSE, &x ); + libblis_test_vobj_randomize( params, FALSE, &y ); + } else { + libblis_test_vobj_irandomize( params, &x ); + libblis_test_vobj_irandomize( params, &y ); + } + + bli_copyv( &y, &y_save ); + + // Apply the parameters. + bli_obj_set_conj( conjx, &x ); + + libblis_test_xpbyv_impl( iface, &x, &beta, &y ); + +#ifndef __GTEST_VALGRIND_TEST__ + if(params->bitrp) { + obj_t r; + + libblis_test_vobj_create( params, datatype, sc_str[1], m, &r ); + + resid = libblis_test_bitrp_xpbyv(params, iface, &x, &beta, &y, + &y_save, &r, datatype); + + bli_obj_free( &r ); + } + else { + resid = libblis_ref_xpbyv( params, &x, &beta, &y, &y_save ); + } +#endif + + // Zero out performance and residual if output vector is empty. + libblis_test_check_empty_problem( &y, &resid ); + + // Free the test objects. + libblis_test_obj_free( &x ); + libblis_test_obj_free( &y ); + libblis_test_obj_free( &y_save ); + + return abs(resid); +} + +void libblis_test_xpbyv_impl ( + iface_t iface, + obj_t* x, + obj_t* beta, + obj_t* y +) { + switch ( iface ) + { + case BLIS_TEST_SEQ_FRONT_END: + bli_xpbyv( x, beta, y ); + break; + + default: + libblis_test_printf_error( "Invalid interface type.\n" ); + } +} + +double libblis_test_xpbyv_check ( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig +) { + num_t dt = bli_obj_dt( y ); + num_t dt_real = bli_obj_dt_proj_to_real( y ); + + dim_t m = bli_obj_vector_dim( y ); + + obj_t x_temp, y_temp; + obj_t norm; + + double junk; + double resid = 0.0; + + // + // Pre-conditions: + // - x is randomized. + // - y_orig is randomized. + // Note: + // - beta should have a non-zero imaginary component in the complex + // cases in order to more fully exercise the implementation. + // + // Under these conditions, we assume that the implementation for + // + // y := beta * y_orig + conjx(x) + // + // is functioning correctly if + // + // normfv( y - ( beta * y_orig + conjx(x) ) ) + // + // is negligible. + // + + bli_obj_scalar_init_detached( dt_real, &norm ); + + bli_obj_create( dt, m, 1, 0, 0, &x_temp ); + bli_obj_create( dt, m, 1, 0, 0, &y_temp ); + + bli_copyv( x, &x_temp ); + bli_copyv( y_orig, &y_temp ); + + bli_scalv( beta, &y_temp ); + bli_addv( &x_temp, &y_temp ); + + bli_subv( &y_temp, y ); + bli_normfv( y, &norm ); + bli_getsc( &norm, &resid, &junk ); + + bli_obj_free( &x_temp ); + bli_obj_free( &y_temp ); + + return resid; +} \ No newline at end of file diff --git a/gtestsuite/src/test_xpbyv.h b/gtestsuite/src/test_xpbyv.h new file mode 100644 index 000000000..f51b8c423 --- /dev/null +++ b/gtestsuite/src/test_xpbyv.h @@ -0,0 +1,17 @@ +#ifndef TEST_XPBYV_H +#define TEST_XPBYV_H + +#include "blis_test.h" + +double libblis_test_ixpbyv_check + ( + test_params_t* params, + obj_t* x, + obj_t* beta, + obj_t* y, + obj_t* y_orig + ); + +double libblis_check_nan_xpbyv( char* sc_str, obj_t* b, num_t dt ); + +#endif /* TEST_XPYV_H */ \ No newline at end of file