From 91a791bbb3d896e248a3242b6cb1509bd22fe03e Mon Sep 17 00:00:00 2001 From: crtrott Date: Thu, 10 Dec 2015 11:52:34 -0700 Subject: [PATCH] Snapshot of kokkos.git from commit 0a776f65e7429b875839719c4fe528c15e871e46 From repository at git@github.com:/kokkos/kokkos.git At commit: commit 0a776f65e7429b875839719c4fe528c15e871e46 Author: crtrott Date: Thu Dec 10 11:51:50 2015 -0700 Adding CUDA 7.5 as secondary compiler to README --- lib/kokkos/.gitignore | 8 + lib/kokkos/CMakeLists.txt | 123 + lib/kokkos/HOW_TO_SNAPSHOT | 73 + lib/kokkos/Makefile.kokkos | 82 +- lib/kokkos/Makefile.targets | 5 + lib/kokkos/README | 8 + lib/kokkos/algorithms/CMakeLists.txt | 10 + .../algorithms/cmake/Dependencies.cmake | 5 + .../cmake/KokkosAlgorithms_config.h.in | 4 + lib/kokkos/algorithms/src/CMakeLists.txt | 21 + lib/kokkos/algorithms/src/Kokkos_Random.hpp | 54 +- .../algorithms/unit_tests/CMakeLists.txt | 38 + lib/kokkos/algorithms/unit_tests/Makefile | 10 +- lib/kokkos/cmake/Dependencies.cmake | 10 + lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake | 75 + lib/kokkos/cmake/tpls/FindTPLHWLOC.cmake | 71 + lib/kokkos/cmake/tpls/FindTPLPthread.cmake | 82 + lib/kokkos/cmake/tpls/FindTPLQTHREAD.cmake | 70 + lib/kokkos/config/nvcc_wrapper | 138 +- lib/kokkos/config/test_all_sandia | 276 +- .../config/testing_scripts/obj_size_opt_check | 287 + lib/kokkos/containers/CMakeLists.txt | 10 + .../containers/cmake/Dependencies.cmake | 5 + .../cmake/KokkosContainers_config.h.in | 4 + .../performance_tests/CMakeLists.txt | 26 + .../containers/performance_tests/Makefile | 10 +- lib/kokkos/containers/src/CMakeLists.txt | 31 + lib/kokkos/containers/src/Kokkos_Bitset.hpp | 4 +- lib/kokkos/containers/src/Kokkos_DualView.hpp | 103 +- lib/kokkos/containers/src/Kokkos_Vector.hpp | 12 +- .../containers/unit_tests/CMakeLists.txt | 40 + lib/kokkos/containers/unit_tests/Makefile | 10 +- .../containers/unit_tests/TestComplex.hpp | 25 +- lib/kokkos/core/CMakeLists.txt | 11 + lib/kokkos/core/cmake/Dependencies.cmake | 4 + lib/kokkos/core/cmake/KokkosCore_config.h.in | 50 + lib/kokkos/core/perf_test/CMakeLists.txt | 18 + lib/kokkos/core/perf_test/Makefile | 10 +- lib/kokkos/core/perf_test/PerfTestCuda.cpp | 2 +- lib/kokkos/core/perf_test/test_atomic.cpp | 16 +- lib/kokkos/core/src/CMakeLists.txt | 113 + .../core/src/Cuda/KokkosExp_Cuda_View.hpp | 53 +- lib/kokkos/core/src/Cuda/Kokkos_CudaExec.hpp | 8 +- lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp | 212 +- .../core/src/Cuda/Kokkos_Cuda_Alloc.hpp | 2 +- .../src/Cuda/Kokkos_Cuda_BasicAllocators.cpp | 6 + .../src/Cuda/Kokkos_Cuda_BasicAllocators.hpp | 5 +- lib/kokkos/core/src/Cuda/Kokkos_Cuda_Impl.cpp | 96 + .../core/src/Cuda/Kokkos_Cuda_Internal.hpp | 95 +- .../core/src/Cuda/Kokkos_Cuda_Parallel.hpp | 774 +- .../core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp | 7 +- lib/kokkos/core/src/Cuda/Kokkos_Cuda_View.hpp | 5 + lib/kokkos/core/src/KokkosExp_View.hpp | 1470 +- lib/kokkos/core/src/Kokkos_Array.hpp | 1 + lib/kokkos/core/src/Kokkos_Complex.hpp | 529 + lib/kokkos/core/src/Kokkos_Core.hpp | 139 +- lib/kokkos/core/src/Kokkos_Core_fwd.hpp | 28 + lib/kokkos/core/src/Kokkos_CudaSpace.hpp | 87 +- lib/kokkos/core/src/Kokkos_ExecPolicy.hpp | 89 +- lib/kokkos/core/src/Kokkos_HBWSpace.hpp | 327 + lib/kokkos/core/src/Kokkos_HostSpace.hpp | 23 +- lib/kokkos/core/src/Kokkos_Layout.hpp | 9 +- lib/kokkos/core/src/Kokkos_Macros.hpp | 6 + lib/kokkos/core/src/Kokkos_MemoryTraits.hpp | 4 +- lib/kokkos/core/src/Kokkos_OpenMP.hpp | 9 +- lib/kokkos/core/src/Kokkos_Parallel.hpp | 64 +- lib/kokkos/core/src/Kokkos_Serial.hpp | 475 +- lib/kokkos/core/src/Kokkos_View.hpp | 52 +- lib/kokkos/core/src/Kokkos_hwloc.hpp | 14 +- lib/kokkos/core/src/Makefile | 14 +- .../src/OpenMP/Kokkos_OpenMP_Parallel.hpp | 601 +- .../core/src/OpenMP/Kokkos_OpenMPexec.cpp | 60 +- .../core/src/OpenMP/Kokkos_OpenMPexec.hpp | 66 +- .../core/src/Qthread/Kokkos_QthreadExec.hpp | 2 +- .../src/Qthread/Kokkos_Qthread_Parallel.hpp | 393 +- .../src/Qthread/Kokkos_Qthread_TaskPolicy.cpp | 105 +- .../src/Qthread/Kokkos_Qthread_TaskPolicy.hpp | 5 +- .../core/src/Threads/Kokkos_ThreadsExec.cpp | 105 +- .../core/src/Threads/Kokkos_ThreadsExec.hpp | 14 + .../src/Threads/Kokkos_ThreadsExec_base.cpp | 1 + .../core/src/Threads/Kokkos_ThreadsTeam.hpp | 57 +- .../src/Threads/Kokkos_Threads_Parallel.hpp | 446 +- lib/kokkos/core/src/impl/CMakeLists.txt | 18 + .../core/src/impl/KokkosExp_SharedAlloc.cpp | 90 +- .../core/src/impl/KokkosExp_SharedAlloc.hpp | 210 +- .../core/src/impl/KokkosExp_ViewAllocProp.hpp | 22 + .../core/src/impl/KokkosExp_ViewArray.hpp | 210 +- .../core/src/impl/KokkosExp_ViewMapping.hpp | 1917 +- .../core/src/impl/KokkosExp_ViewTile.hpp | 89 +- .../src/impl/Kokkos_AllocationTracker.cpp | 4 + .../src/impl/Kokkos_AllocationTracker.hpp | 30 +- .../core/src/impl/Kokkos_Atomic_View.hpp | 4 + .../core/src/impl/Kokkos_Atomic_Windows.hpp | 33 +- .../core/src/impl/Kokkos_BasicAllocators.cpp | 14 +- .../core/src/impl/Kokkos_BasicAllocators.hpp | 3 + lib/kokkos/core/src/impl/Kokkos_Core.cpp | 7 + lib/kokkos/core/src/impl/Kokkos_Error.cpp | 2 +- lib/kokkos/core/src/impl/Kokkos_Error.hpp | 4 + .../core/src/impl/Kokkos_HBWAllocators.cpp | 108 + .../core/src/impl/Kokkos_HBWAllocators.hpp | 75 + lib/kokkos/core/src/impl/Kokkos_HBWSpace.cpp | 397 + lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp | 152 +- .../src/impl/Kokkos_Profiling_Interface.cpp | 18 +- .../src/impl/Kokkos_Serial_TaskPolicy.cpp | 2 +- lib/kokkos/core/src/impl/Kokkos_Shape.hpp | 16 +- .../core/src/impl/Kokkos_Synchronic.hpp | 693 + .../src/impl/Kokkos_Synchronic_Config.hpp | 169 + .../core/src/impl/Kokkos_Synchronic_n3998.hpp | 162 + lib/kokkos/core/src/impl/Kokkos_Tags.hpp | 30 +- lib/kokkos/core/src/impl/Kokkos_Traits.hpp | 72 +- .../core/src/impl/Kokkos_ViewDefault.hpp | 18 +- .../core/src/impl/Kokkos_ViewSupport.hpp | 4 + .../core/src/impl/Kokkos_ViewTileLeft.hpp | 4 +- lib/kokkos/core/src/impl/Kokkos_hwloc.cpp | 30 +- lib/kokkos/core/src/impl/Kokkos_spinwait.cpp | 9 +- lib/kokkos/core/unit_test/CMakeLists.txt | 102 + lib/kokkos/core/unit_test/Makefile | 34 +- lib/kokkos/core/unit_test/TestAggregate.hpp | 33 +- .../core/unit_test/TestAllocationTracker.cpp | 16 +- lib/kokkos/core/unit_test/TestAtomic.hpp | 16 +- lib/kokkos/core/unit_test/TestCuda.cpp | 26 +- .../core/unit_test/TestDefaultDeviceType.cpp | 2 +- lib/kokkos/core/unit_test/TestOpenMP.cpp | 10 + lib/kokkos/core/unit_test/TestQthread.cpp | 6 + lib/kokkos/core/unit_test/TestSerial.cpp | 6 + lib/kokkos/core/unit_test/TestSharedAlloc.hpp | 16 +- lib/kokkos/core/unit_test/TestSynchronic.cpp | 448 + lib/kokkos/core/unit_test/TestSynchronic.hpp | 240 + lib/kokkos/core/unit_test/TestTeam.hpp | 65 + lib/kokkos/core/unit_test/TestThreads.cpp | 22 + lib/kokkos/core/unit_test/TestViewAPI.hpp | 38 +- lib/kokkos/core/unit_test/TestViewMapping.hpp | 268 +- lib/kokkos/core/unit_test/TestViewOfClass.hpp | 83 +- lib/kokkos/core/unit_test/TestViewSubview.hpp | 9 + lib/kokkos/doc/Doxyfile | 127 + lib/kokkos/doc/Kokkos_PG.pdf | Bin 0 -> 1359256 bytes lib/kokkos/doc/README | 32 + lib/kokkos/doc/build_docs | 15 + lib/kokkos/doc/index.doc | 72 + lib/kokkos/example/CMakeLists.txt | 20 + lib/kokkos/example/cmake/Dependencies.cmake | 4 + lib/kokkos/example/feint/CMakeLists.txt | 18 + lib/kokkos/example/fenl/CMakeLists.txt | 17 + lib/kokkos/example/fenl/Makefile | 23 +- lib/kokkos/example/fixture/CMakeLists.txt | 13 + .../example/global_2_local_ids/CMakeLists.txt | 17 + .../example/global_2_local_ids/Makefile | 53 + lib/kokkos/example/grow_array/CMakeLists.txt | 14 + lib/kokkos/example/grow_array/Makefile | 53 + lib/kokkos/example/md_skeleton/CMakeLists.txt | 16 + lib/kokkos/example/md_skeleton/Makefile | 53 + lib/kokkos/example/multi_fem/CMakeLists.txt | 16 + lib/kokkos/example/multi_fem/Makefile | 53 + .../example/query_device/CMakeLists.txt | 14 + lib/kokkos/example/query_device/Makefile | 53 + lib/kokkos/example/sort_array/CMakeLists.txt | 15 + lib/kokkos/example/sort_array/Makefile | 53 + .../tutorial/01_hello_world/CMakeLists.txt | 11 + .../01_hello_world_lambda/CMakeLists.txt | 13 + .../tutorial/02_simple_reduce/CMakeLists.txt | 10 + .../02_simple_reduce_lambda/CMakeLists.txt | 12 + .../tutorial/03_simple_view/CMakeLists.txt | 10 + .../03_simple_view_lambda/CMakeLists.txt | 12 + .../04_simple_memoryspaces/CMakeLists.txt | 10 + .../tutorial/05_simple_atomics/CMakeLists.txt | 10 + .../01_data_layouts/CMakeLists.txt | 10 + .../02_memory_traits/CMakeLists.txt | 10 + .../Advanced_Views/03_subviews/CMakeLists.txt | 10 + .../04_dualviews/CMakeLists.txt | 10 + .../05_NVIDIA_UVM/CMakeLists.txt | 13 + .../tutorial/Advanced_Views/CMakeLists.txt | 9 + lib/kokkos/example/tutorial/CMakeLists.txt | 17 + .../01_thread_teams/CMakeLists.txt | 10 + .../01_thread_teams_lambda/CMakeLists.txt | 13 + .../02_nested_parallel_for/CMakeLists.txt | 10 + .../03_vectorization/CMakeLists.txt | 16 + .../04_team_scan/CMakeLists.txt | 10 + .../Hierarchical_Parallelism/CMakeLists.txt | 8 + lib/kokkos/generate_makefile.bash | 8 + lib/kokkos/tpls/gtest/gtest/LICENSE | 28 + lib/kokkos/tpls/gtest/gtest/README | 13 + lib/kokkos/tpls/gtest/gtest/gtest-all.cc | 9594 ++++++++ lib/kokkos/tpls/gtest/gtest/gtest-test-part.h | 1 + lib/kokkos/tpls/gtest/gtest/gtest.h | 20065 ++++++++++++++++ 184 files changed, 41315 insertions(+), 3847 deletions(-) create mode 100644 lib/kokkos/.gitignore create mode 100644 lib/kokkos/CMakeLists.txt create mode 100644 lib/kokkos/HOW_TO_SNAPSHOT create mode 100644 lib/kokkos/algorithms/CMakeLists.txt create mode 100644 lib/kokkos/algorithms/cmake/Dependencies.cmake create mode 100644 lib/kokkos/algorithms/cmake/KokkosAlgorithms_config.h.in create mode 100644 lib/kokkos/algorithms/src/CMakeLists.txt create mode 100644 lib/kokkos/algorithms/unit_tests/CMakeLists.txt create mode 100644 lib/kokkos/cmake/Dependencies.cmake create mode 100644 lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake create mode 100644 lib/kokkos/cmake/tpls/FindTPLHWLOC.cmake create mode 100644 lib/kokkos/cmake/tpls/FindTPLPthread.cmake create mode 100644 lib/kokkos/cmake/tpls/FindTPLQTHREAD.cmake create mode 100755 lib/kokkos/config/testing_scripts/obj_size_opt_check create mode 100644 lib/kokkos/containers/CMakeLists.txt create mode 100644 lib/kokkos/containers/cmake/Dependencies.cmake create mode 100644 lib/kokkos/containers/cmake/KokkosContainers_config.h.in create mode 100644 lib/kokkos/containers/performance_tests/CMakeLists.txt create mode 100644 lib/kokkos/containers/src/CMakeLists.txt create mode 100644 lib/kokkos/containers/unit_tests/CMakeLists.txt create mode 100644 lib/kokkos/core/CMakeLists.txt create mode 100644 lib/kokkos/core/cmake/Dependencies.cmake create mode 100644 lib/kokkos/core/cmake/KokkosCore_config.h.in create mode 100644 lib/kokkos/core/perf_test/CMakeLists.txt create mode 100644 lib/kokkos/core/src/CMakeLists.txt create mode 100644 lib/kokkos/core/src/Kokkos_Complex.hpp create mode 100644 lib/kokkos/core/src/Kokkos_HBWSpace.hpp create mode 100644 lib/kokkos/core/src/impl/CMakeLists.txt create mode 100644 lib/kokkos/core/src/impl/Kokkos_HBWAllocators.cpp create mode 100644 lib/kokkos/core/src/impl/Kokkos_HBWAllocators.hpp create mode 100644 lib/kokkos/core/src/impl/Kokkos_HBWSpace.cpp create mode 100644 lib/kokkos/core/src/impl/Kokkos_Synchronic.hpp create mode 100644 lib/kokkos/core/src/impl/Kokkos_Synchronic_Config.hpp create mode 100644 lib/kokkos/core/src/impl/Kokkos_Synchronic_n3998.hpp create mode 100644 lib/kokkos/core/unit_test/CMakeLists.txt create mode 100644 lib/kokkos/core/unit_test/TestSynchronic.cpp create mode 100644 lib/kokkos/core/unit_test/TestSynchronic.hpp create mode 100644 lib/kokkos/doc/Doxyfile create mode 100644 lib/kokkos/doc/Kokkos_PG.pdf create mode 100644 lib/kokkos/doc/README create mode 100755 lib/kokkos/doc/build_docs create mode 100644 lib/kokkos/doc/index.doc create mode 100644 lib/kokkos/example/CMakeLists.txt create mode 100644 lib/kokkos/example/cmake/Dependencies.cmake create mode 100644 lib/kokkos/example/feint/CMakeLists.txt create mode 100644 lib/kokkos/example/fenl/CMakeLists.txt create mode 100644 lib/kokkos/example/fixture/CMakeLists.txt create mode 100644 lib/kokkos/example/global_2_local_ids/CMakeLists.txt create mode 100644 lib/kokkos/example/global_2_local_ids/Makefile create mode 100644 lib/kokkos/example/grow_array/CMakeLists.txt create mode 100644 lib/kokkos/example/grow_array/Makefile create mode 100644 lib/kokkos/example/md_skeleton/CMakeLists.txt create mode 100644 lib/kokkos/example/md_skeleton/Makefile create mode 100644 lib/kokkos/example/multi_fem/CMakeLists.txt create mode 100644 lib/kokkos/example/multi_fem/Makefile create mode 100644 lib/kokkos/example/query_device/CMakeLists.txt create mode 100644 lib/kokkos/example/query_device/Makefile create mode 100644 lib/kokkos/example/sort_array/CMakeLists.txt create mode 100644 lib/kokkos/example/sort_array/Makefile create mode 100644 lib/kokkos/example/tutorial/01_hello_world/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/01_hello_world_lambda/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/02_simple_reduce/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/02_simple_reduce_lambda/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/03_simple_view/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/03_simple_view_lambda/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/04_simple_memoryspaces/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/05_simple_atomics/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Advanced_Views/01_data_layouts/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Advanced_Views/02_memory_traits/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Advanced_Views/03_subviews/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Advanced_Views/04_dualviews/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Advanced_Views/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams_lambda/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Hierarchical_Parallelism/02_nested_parallel_for/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Hierarchical_Parallelism/03_vectorization/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/CMakeLists.txt create mode 100644 lib/kokkos/example/tutorial/Hierarchical_Parallelism/CMakeLists.txt create mode 100644 lib/kokkos/tpls/gtest/gtest/LICENSE create mode 100644 lib/kokkos/tpls/gtest/gtest/README create mode 100644 lib/kokkos/tpls/gtest/gtest/gtest-all.cc create mode 120000 lib/kokkos/tpls/gtest/gtest/gtest-test-part.h create mode 100644 lib/kokkos/tpls/gtest/gtest/gtest.h diff --git a/lib/kokkos/.gitignore b/lib/kokkos/.gitignore new file mode 100644 index 0000000000..f9d16be155 --- /dev/null +++ b/lib/kokkos/.gitignore @@ -0,0 +1,8 @@ +# Standard ignores +*~ +*.pyc +\#*# +.#* +.*.swp +.cproject +.project diff --git a/lib/kokkos/CMakeLists.txt b/lib/kokkos/CMakeLists.txt new file mode 100644 index 0000000000..0d437c0f8f --- /dev/null +++ b/lib/kokkos/CMakeLists.txt @@ -0,0 +1,123 @@ + +# +# A) Forward delcare the package so that certain options are also defined for +# subpackages +# + +TRIBITS_PACKAGE_DECL(Kokkos) # ENABLE_SHADOWING_WARNINGS) + +#------------------------------------------------------------------------------ +# +# B) Define the common options for Kokkos first so they can be used by +# subpackages as well. +# + +TRIBITS_ADD_DEBUG_OPTION() + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_SIERRA_BUILD + KOKKOS_FOR_SIERRA + "Configure Kokkos for building within the Sierra build system." + OFF + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_Cuda + KOKKOS_HAVE_CUDA + "Enable CUDA support in Kokkos." + "${TPL_ENABLE_CUDA}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_Cuda_UVM + KOKKOS_USE_CUDA_UVM + "Enable CUDA Unified Virtual Memory support in Kokkos." + OFF + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_Pthread + KOKKOS_HAVE_PTHREAD + "Enable Pthread support in Kokkos." + "${TPL_ENABLE_Pthread}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_OpenMP + KOKKOS_HAVE_OPENMP + "Enable OpenMP support in Kokkos." + "${${PROJECT_NAME}_ENABLE_OpenMP}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_QTHREAD + KOKKOS_HAVE_QTHREAD + "Enable QTHREAD support in Kokkos." + "${TPL_ENABLE_QTHREAD}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_CXX11 + KOKKOS_HAVE_CXX11 + "Enable C++11 support in Kokkos." + "${${PROJECT_NAME}_ENABLE_CXX11}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_HWLOC + KOKKOS_HAVE_HWLOC + "Enable HWLOC support in Kokkos." + "${TPL_ENABLE_HWLOC}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_MPI + KOKKOS_HAVE_MPI + "Enable MPI support in Kokkos." + "${TPL_ENABLE_MPI}" + ) + +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_Debug_Bounds_Check + KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK + "Enable bounds checking support in Kokkos." + OFF + ) + +#TRIBITS_ADD_OPTION_AND_DEFINE( +# Kokkos_ENABLE_Profiling_Collect_Kernel_Data +# KOKKOS_ENABLE_PROFILING_COLLECT_KERNEL_DATA +# "Enable profiling support for kernel data collections in Kokkos." +# "${${PROJECT_NAME}_ENABLE_KokkosProfiler}" +# ) + +# placeholder for future device... +TRIBITS_ADD_OPTION_AND_DEFINE( + Kokkos_ENABLE_Winthread + KOKKOS_HAVE_WINTHREAD + "Enable Winthread support in Kokkos." + "${TPL_ENABLE_Winthread}" + ) + +#------------------------------------------------------------------------------ +# +# C) Process the subpackages for Kokkos +# + +TRIBITS_PROCESS_SUBPACKAGES() + +# +# D) If Kokkos itself is enabled, process the Kokkos package +# + +TRIBITS_PACKAGE_DEF() + +TRIBITS_EXCLUDE_AUTOTOOLS_FILES() + +TRIBITS_EXCLUDE_FILES( + classic/doc + classic/LinAlg/doc/CrsRefactorNotesMay2012 + ) + +TRIBITS_PACKAGE_POSTPROCESS() + diff --git a/lib/kokkos/HOW_TO_SNAPSHOT b/lib/kokkos/HOW_TO_SNAPSHOT new file mode 100644 index 0000000000..46bfb4167f --- /dev/null +++ b/lib/kokkos/HOW_TO_SNAPSHOT @@ -0,0 +1,73 @@ + +Developers of Kokkos (those who commit modifications to Kokkos) +must maintain the snapshot of Kokkos in the Trilinos repository. + +This file contains instructions for how to +snapshot Kokkos from github.com/kokkos to Trilinos. + +------------------------------------------------------------------------ +*** EVERYTHING GOES RIGHT WORKFLOW *** + +1) Given a 'git clone' of Kokkos and of Trilinos repositories. +1.1) Let ${KOKKOS} be the absolute path to the Kokkos clone. + This path *must* terminate with the directory name 'kokkos'; + e.g., ${HOME}/kokkos . +1.2) Let ${TRILINOS} be the absolute path to the Trilinos directory. + +2) Given that the Kokkos build & test is clean and + changes are committed to the Kokkos clone. + +3) Snapshot the current commit in the Kokkos clone into the Trilinos clone. + This overwrites ${TRILINOS}/packages/kokkos with the content of ${KOKKOS}: + ${KOKKOS}/config/snapshot.py --verbose ${KOKKOS} ${TRILINOS}/packages + +4) Verify the snapshot commit happened as expected + cd ${TRILINOS}/packages/kokkos + git log -1 --name-only + +5) Modify, build, and test Trilinos with the Kokkos snapshot. + +6) Given that that the Trilinos build & test is clean and + changes are committed to the Trilinos clone. + +7) Attempt push to the Kokkos repository. + If push fails then you must 'remove the Kokkos snapshot' + from your Trilinos clone. + See below. + +8) Attempt to push to the Trilinos repository. + If updating for a failed push requires you to change Kokkos you must + 'remove the Kokkos snapshot' from your Trilinos clone. + See below. + +------------------------------------------------------------------------ +*** WHEN SOMETHING GOES WRONG AND YOU MUST *** +*** REMOVE THE KOKKOS SNAPSHOT FROM YOUR TRILINOS CLONE *** + +1) Query the Trilinos clone commit log. + git log --oneline + +2) Note the of the commit to the Trillinos clone + immediately BEFORE the Kokkos snapshot commit. + Copy this for use in the next command. + +3) IF more than one outstanding commit then you can remove just the + Kokkos snapshot commit with 'git rebase -i'. Edit the rebase file. + Remove or comment out the Kokkos snapshot commit entry. + git rebase -i + +4) IF the Kokkos snapshot commit is the one and only + outstanding commit then remove just than commit. + git reset --hard HEAD~1 + +------------------------------------------------------------------------ +*** REGARDING 'snapshot.py' TOOL *** + +The 'snapshot.py' tool is developed and maintained by the +Center for Computing Research (CCR) +Software Engineering, Maintenance, and Support (SEMS) team. + +Contact Brent Perschbacher for questions> + +------------------------------------------------------------------------ + diff --git a/lib/kokkos/Makefile.kokkos b/lib/kokkos/Makefile.kokkos index 30ecec3364..cf6d3f3971 100644 --- a/lib/kokkos/Makefile.kokkos +++ b/lib/kokkos/Makefile.kokkos @@ -1,20 +1,18 @@ # Default settings common options -KOKKOS_PATH=../../lib/kokkos - #Options: OpenMP,Serial,Pthreads,Cuda -KOKKOS_DEVICES ?= "OpenMP" -#KOKKOS_DEVICES ?= "Pthreads" -#Options: KNC,SNB,HSW,Kepler,Kepler30,Kepler32,Kepler35,Kepler37,Maxwell,Maxwell50,Maxwell52,Maxwell53,ARMv8,BGQ,Power7,Power8 +#KOKKOS_DEVICES ?= "OpenMP" +KOKKOS_DEVICES ?= "Pthreads" +#Options: KNC,SNB,HSW,Kepler,Kepler30,Kepler32,Kepler35,Kepler37,Maxwell,Maxwell50,Maxwell52,Maxwell53,ARMv8,BGQ,Power7,Power8,KNL KOKKOS_ARCH ?= "" #Options: yes,no KOKKOS_DEBUG ?= "no" -#Options: hwloc,librt +#Options: hwloc,librt,experimental_memkind KOKKOS_USE_TPLS ?= "" #Options: c++11 KOKKOS_CXX_STANDARD ?= "c++11" -#Options: kernel_times,aggregate_mpi -KOKKOS_PROFILING ?= "" +#Options: aggressive_vectorization +KOKKOS_OPTIONS ?= "" #Default settings specific options #Options: force_uvm,use_ldg,rdc,enable_lambda @@ -30,8 +28,10 @@ KOKKOS_INTERNAL_ENABLE_CXX11 := $(strip $(shell echo $(KOKKOS_CXX_STANDARD) | gr # Check for external libraries KOKKOS_INTERNAL_USE_HWLOC := $(strip $(shell echo $(KOKKOS_USE_TPLS) | grep "hwloc" | wc -l)) KOKKOS_INTERNAL_USE_LIBRT := $(strip $(shell echo $(KOKKOS_USE_TPLS) | grep "librt" | wc -l)) +KOKKOS_INTERNAL_USE_MEMKIND := $(strip $(shell echo $(KOKKOS_USE_TPLS) | grep "experimental_memkind" | wc -l)) # Check for advanced settings +KOKKOS_INTERNAL_OPT_RANGE_AGGRESSIVE_VECTORIZATION := $(strip $(shell echo $(KOKKOS_OPTIONS) | grep "aggressive_vectorization" | wc -l)) KOKKOS_INTERNAL_CUDA_USE_LDG := $(strip $(shell echo $(KOKKOS_CUDA_OPTIONS) | grep "use_ldg" | wc -l)) KOKKOS_INTERNAL_CUDA_USE_UVM := $(strip $(shell echo $(KOKKOS_CUDA_OPTIONS) | grep "force_uvm" | wc -l)) KOKKOS_INTERNAL_CUDA_USE_RELOC := $(strip $(shell echo $(KOKKOS_CUDA_OPTIONS) | grep "rdc" | wc -l)) @@ -50,10 +50,11 @@ ifeq ($(KOKKOS_INTERNAL_USE_PTHREADS), 0) endif endif -KOKKOS_INTERNAL_COMPILER_PGI := $(shell $(CXX) --version 2>&1 | grep PGI | wc -l) -KOKKOS_INTERNAL_COMPILER_XL := $(shell $(CXX) -qversion 2>&1 | grep XL | wc -l) -KOKKOS_INTERNAL_COMPILER_CRAY := $(shell $(CXX) -craype-verbose 2>&1 | grep "CC-" | wc -l) -KOKKOS_INTERNAL_OS_CYGWIN := $(shell uname | grep CYGWIN | wc -l) +KOKKOS_INTERNAL_COMPILER_INTEL := $(shell $(CXX) --version 2>&1 | grep "Intel Corporation" | wc -l) +KOKKOS_INTERNAL_COMPILER_PGI := $(shell $(CXX) --version 2>&1 | grep PGI | wc -l) +KOKKOS_INTERNAL_COMPILER_XL := $(shell $(CXX) -qversion 2>&1 | grep XL | wc -l) +KOKKOS_INTERNAL_COMPILER_CRAY := $(shell $(CXX) -craype-verbose 2>&1 | grep "CC-" | wc -l) +KOKKOS_INTERNAL_OS_CYGWIN := $(shell uname | grep CYGWIN | wc -l) ifeq ($(KOKKOS_INTERNAL_COMPILER_PGI), 1) KOKKOS_INTERNAL_OPENMP_FLAG := -mp @@ -93,8 +94,10 @@ KOKKOS_INTERNAL_USE_CUDA := $(strip $(shell echo $(KOKKOS_DEVICES) | grep Cuda | KOKKOS_INTERNAL_USE_ARCH_KNC := $(strip $(shell echo $(KOKKOS_ARCH) | grep KNC | wc -l)) KOKKOS_INTERNAL_USE_ARCH_SNB := $(strip $(shell echo $(KOKKOS_ARCH) | grep SNB | wc -l)) KOKKOS_INTERNAL_USE_ARCH_HSW := $(strip $(shell echo $(KOKKOS_ARCH) | grep HSW | wc -l)) +KOKKOS_INTERNAL_USE_ARCH_KNL := $(strip $(shell echo $(KOKKOS_ARCH) | grep KNL | wc -l)) #NVIDIA based +NVCC_WRAPPER := $(KOKKOS_PATH)/config/nvcc_wrapper KOKKOS_INTERNAL_USE_ARCH_KEPLER30 := $(strip $(shell echo $(KOKKOS_ARCH) | grep Kepler30 | wc -l)) KOKKOS_INTERNAL_USE_ARCH_KEPLER32 := $(strip $(shell echo $(KOKKOS_ARCH) | grep Kepler32 | wc -l)) KOKKOS_INTERNAL_USE_ARCH_KEPLER35 := $(strip $(shell echo $(KOKKOS_ARCH) | grep Kepler35 | wc -l)) @@ -135,8 +138,9 @@ KOKKOS_INTERNAL_USE_ARCH_IBM := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_ KOKKOS_INTERNAL_USE_ARCH_AMDAVX := $(strip $(shell echo $(KOKKOS_ARCH) | grep AMDAVX | wc -l)) #Any AVX? -KOKKOS_INTERNAL_USE_ARCH_AVX := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_SNB)+$(KOKKOS_INTERNAL_USE_ARCH_AMDAVX) | bc )) -KOKKOS_INTERNAL_USE_ARCH_AVX2 := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_HSW) | bc )) +KOKKOS_INTERNAL_USE_ARCH_AVX := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_SNB)+$(KOKKOS_INTERNAL_USE_ARCH_AMDAVX) | bc )) +KOKKOS_INTERNAL_USE_ARCH_AVX2 := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_HSW) | bc )) +KOKKOS_INTERNAL_USE_ARCH_AVX512MIC := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_KNL) | bc )) #Incompatible flags? KOKKOS_INTERNAL_USE_ARCH_MULTIHOST := $(strip $(shell echo "$(KOKKOS_INTERNAL_USE_ARCH_AVX)+$(KOKKOS_INTERNAL_USE_ARCH_AVX2)+$(KOKKOS_INTERNAL_USE_ARCH_KNC)+$(KOKKOS_INTERNAL_USE_ARCH_IBM)+$(KOKKOS_INTERNAL_USE_ARCH_AMDAVX)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV80)>1" | bc )) @@ -225,6 +229,19 @@ ifeq ($(KOKKOS_INTERNAL_USE_LIBRT), 1) KOKKOS_LIBS += -lrt endif +ifeq ($(KOKKOS_INTERNAL_USE_MEMKIND), 1) + KOKKOS_CPPFLAGS += -I$(MEMKIND_PATH)/include + KOKKOS_LDFLAGS += -L$(MEMKIND_PATH)/lib + KOKKOS_LIBS += -lmemkind + tmp := $(shell echo "\#define KOKKOS_HAVE_HBWSPACE 1" >> KokkosCore_config.tmp ) +endif + +tmp := $(shell echo "/* Optimization Settings */" >> KokkosCore_config.tmp) + +ifeq ($(KOKKOS_INTERNAL_OPT_RANGE_AGGRESSIVE_VECTORIZATION), 1) + tmp := $(shell echo "\#define KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION 1" >> KokkosCore_config.tmp ) +endif + tmp := $(shell echo "/* Cuda Settings */" >> KokkosCore_config.tmp) ifeq ($(KOKKOS_INTERNAL_CUDA_USE_LDG), 1) @@ -265,8 +282,41 @@ ifeq ($(KOKKOS_INTERNAL_USE_ARCH_POWER8), 1) endif ifeq ($(KOKKOS_INTERNAL_USE_ARCH_AVX2), 1) - KOKKOS_CXXFLAGS += -march=core-avx2 - KOKKOS_LDFLAGS += -march=core-avx2 + ifeq ($(KOKKOS_INTERNAL_COMPILER_INTEL), 1) + KOKKOS_CXXFLAGS += -xCORE-AVX2 + KOKKOS_LDFLAGS += -xCORE-AVX2 + else + ifeq ($(KOKKOS_INTERNAL_COMPILER_CRAY), 1) + + else + ifeq ($(KOKKOS_INTERNAL_COMPILER_PGI), 1) + + else + # Assume that this is a really a GNU compiler + KOKKOS_CXXFLAGS += -march=core-avx2 + KOKKOS_LDFLAGS += -march=core-avx2 + endif + endif + endif +endif + +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_AVX512MIC), 1) + ifeq ($(KOKKOS_INTERNAL_COMPILER_INTEL), 1) + KOKKOS_CXXFLAGS += -xMIC-AVX512 + KOKKOS_LDFLAGS += -xMIC-AVX512 + else + ifeq ($(KOKKOS_INTERNAL_COMPILER_CRAY), 1) + + else + ifeq ($(KOKKOS_INTERNAL_COMPILER_PGI), 1) + + else + # Asssume that this is really a GNU compiler + KOKKOS_CXXFLAGS += -march=knl + KOKKOS_LDFLAGS += -march=knl + endif + endif + endif endif ifeq ($(KOKKOS_INTERNAL_USE_ARCH_KNC), 1) diff --git a/lib/kokkos/Makefile.targets b/lib/kokkos/Makefile.targets index 009adb42c0..7574aeb338 100644 --- a/lib/kokkos/Makefile.targets +++ b/lib/kokkos/Makefile.targets @@ -55,3 +55,8 @@ Kokkos_OpenMPexec.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/OpenMP/Kokkos $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/OpenMP/Kokkos_OpenMPexec.cpp endif +Kokkos_HBWSpace.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/impl/Kokkos_HBWSpace.cpp + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/impl/Kokkos_HBWSpace.cpp +Kokkos_HBWAllocators.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/impl/Kokkos_HBWAllocators.cpp + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/impl/Kokkos_HBWAllocators.cpp + diff --git a/lib/kokkos/README b/lib/kokkos/README index 85bd0142bf..904e39abf5 100644 --- a/lib/kokkos/README +++ b/lib/kokkos/README @@ -20,6 +20,13 @@ GTC 2015: A programming guide can be found under doc/Kokkos_PG.pdf. This is an initial version and feedback is greatly appreciated. +A separate repository with extensive tutorial material can be found under +https://github.com/kokkos/kokkos-tutorials. + +If you have a patch to contribute please feel free to issue a pull request against +the develop branch. For major contributions it is better to contact us first +for guidance. + For questions please send an email to kokkos-users@software.sandia.gov @@ -43,6 +50,7 @@ Primary tested compilers are: Secondary tested compilers are: CUDA 6.5 (with gcc 4.7.2) CUDA 7.0 (with gcc 4.7.2) + CUDA 7.5 (with gcc 4.7.2) Other compilers working: PGI 15.4 diff --git a/lib/kokkos/algorithms/CMakeLists.txt b/lib/kokkos/algorithms/CMakeLists.txt new file mode 100644 index 0000000000..7853184a54 --- /dev/null +++ b/lib/kokkos/algorithms/CMakeLists.txt @@ -0,0 +1,10 @@ + + +TRIBITS_SUBPACKAGE(Algorithms) + +ADD_SUBDIRECTORY(src) + +TRIBITS_ADD_TEST_DIRECTORIES(unit_tests) +#TRIBITS_ADD_TEST_DIRECTORIES(performance_tests) + +TRIBITS_SUBPACKAGE_POSTPROCESS() diff --git a/lib/kokkos/algorithms/cmake/Dependencies.cmake b/lib/kokkos/algorithms/cmake/Dependencies.cmake new file mode 100644 index 0000000000..1d71d8af34 --- /dev/null +++ b/lib/kokkos/algorithms/cmake/Dependencies.cmake @@ -0,0 +1,5 @@ +TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( + LIB_REQUIRED_PACKAGES KokkosCore + LIB_OPTIONAL_TPLS Pthread CUDA HWLOC + TEST_OPTIONAL_TPLS CUSPARSE + ) diff --git a/lib/kokkos/algorithms/cmake/KokkosAlgorithms_config.h.in b/lib/kokkos/algorithms/cmake/KokkosAlgorithms_config.h.in new file mode 100644 index 0000000000..67334b70f3 --- /dev/null +++ b/lib/kokkos/algorithms/cmake/KokkosAlgorithms_config.h.in @@ -0,0 +1,4 @@ +#ifndef KOKKOS_ALGORITHMS_CONFIG_H +#define KOKKOS_ALGORITHMS_CONFIG_H + +#endif diff --git a/lib/kokkos/algorithms/src/CMakeLists.txt b/lib/kokkos/algorithms/src/CMakeLists.txt new file mode 100644 index 0000000000..dfbf3323c2 --- /dev/null +++ b/lib/kokkos/algorithms/src/CMakeLists.txt @@ -0,0 +1,21 @@ + +TRIBITS_CONFIGURE_FILE(${PACKAGE_NAME}_config.h) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +#----------------------------------------------------------------------------- + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) +LIST(APPEND HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}_config.h) + +#----------------------------------------------------------------------------- + +TRIBITS_ADD_LIBRARY( + kokkosalgorithms + HEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS + ) + diff --git a/lib/kokkos/algorithms/src/Kokkos_Random.hpp b/lib/kokkos/algorithms/src/Kokkos_Random.hpp index 17f5e073c3..192b1d64f8 100644 --- a/lib/kokkos/algorithms/src/Kokkos_Random.hpp +++ b/lib/kokkos/algorithms/src/Kokkos_Random.hpp @@ -45,7 +45,7 @@ #define KOKKOS_RANDOM_HPP #include -//#include +#include #include #include #include @@ -475,6 +475,58 @@ namespace Kokkos { }; + template + struct rand > { + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex max () { + return ::Kokkos::complex (1.0, 1.0); + } + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex draw (Generator& gen) { + const float re = gen.frand (); + const float im = gen.frand (); + return ::Kokkos::complex (re, im); + } + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex draw (Generator& gen, const ::Kokkos::complex& range) { + const float re = gen.frand (real (range)); + const float im = gen.frand (imag (range)); + return ::Kokkos::complex (re, im); + } + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex draw (Generator& gen, const ::Kokkos::complex& start, const ::Kokkos::complex& end) { + const float re = gen.frand (real (start), real (end)); + const float im = gen.frand (imag (start), imag (end)); + return ::Kokkos::complex (re, im); + } + }; + + template + struct rand > { + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex max () { + return ::Kokkos::complex (1.0, 1.0); + } + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex draw (Generator& gen) { + const double re = gen.drand (); + const double im = gen.drand (); + return ::Kokkos::complex (re, im); + } + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex draw (Generator& gen, const ::Kokkos::complex& range) { + const double re = gen.drand (real (range)); + const double im = gen.drand (imag (range)); + return ::Kokkos::complex (re, im); + } + KOKKOS_INLINE_FUNCTION + static ::Kokkos::complex draw (Generator& gen, const ::Kokkos::complex& start, const ::Kokkos::complex& end) { + const double re = gen.drand (real (start), real (end)); + const double im = gen.drand (imag (start), imag (end)); + return ::Kokkos::complex (re, im); + } + }; + template class Random_XorShift64_Pool; diff --git a/lib/kokkos/algorithms/unit_tests/CMakeLists.txt b/lib/kokkos/algorithms/unit_tests/CMakeLists.txt new file mode 100644 index 0000000000..654104b44e --- /dev/null +++ b/lib/kokkos/algorithms/unit_tests/CMakeLists.txt @@ -0,0 +1,38 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../src ) + +SET(SOURCES + UnitTestMain.cpp + TestCuda.cpp + ) + +SET(LIBRARIES kokkoscore) + +IF(Kokkos_ENABLE_OpenMP) + LIST( APPEND SOURCES + TestOpenMP.cpp + ) +ENDIF() + +IF(Kokkos_ENABLE_Serial) + LIST( APPEND SOURCES + TestSerial.cpp + ) +ENDIF() + +IF(Kokkos_ENABLE_Pthread) + LIST( APPEND SOURCES + TestThreads.cpp + ) +ENDIF() + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest + SOURCES ${SOURCES} + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) diff --git a/lib/kokkos/algorithms/unit_tests/Makefile b/lib/kokkos/algorithms/unit_tests/Makefile index 5fc94ac0f8..5d79364c52 100644 --- a/lib/kokkos/algorithms/unit_tests/Makefile +++ b/lib/kokkos/algorithms/unit_tests/Makefile @@ -6,12 +6,12 @@ vpath %.cpp ${KOKKOS_PATH}/algorithms/unit_tests default: build_all echo "End Build" - + include $(KOKKOS_PATH)/Makefile.kokkos ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) - CXX = nvcc_wrapper + CXX = $(NVCC_WRAPPER) CXXFLAGS ?= -O3 LINK = $(CXX) LDFLAGS ?= -lpthread @@ -56,7 +56,7 @@ KokkosAlgorithms_UnitTest_Cuda: $(OBJ_CUDA) $(KOKKOS_LINK_DEPENDS) KokkosAlgorithms_UnitTest_Threads: $(OBJ_THREADS) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_THREADS) $(KOKKOS_LIBS) $(LIB) -o KokkosAlgorithms_UnitTest_Threads - + KokkosAlgorithms_UnitTest_OpenMP: $(OBJ_OPENMP) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_OPENMP) $(KOKKOS_LIBS) $(LIB) -o KokkosAlgorithms_UnitTest_OpenMP @@ -74,11 +74,11 @@ test-openmp: KokkosAlgorithms_UnitTest_OpenMP test-serial: KokkosAlgorithms_UnitTest_Serial ./KokkosAlgorithms_UnitTest_Serial - + build_all: $(TARGETS) test: $(TEST_TARGETS) - + clean: kokkos-clean rm -f *.o $(TARGETS) diff --git a/lib/kokkos/cmake/Dependencies.cmake b/lib/kokkos/cmake/Dependencies.cmake new file mode 100644 index 0000000000..8c51eab4d7 --- /dev/null +++ b/lib/kokkos/cmake/Dependencies.cmake @@ -0,0 +1,10 @@ +TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( + SUBPACKAGES_DIRS_CLASSIFICATIONS_OPTREQS + #SubPackageName Directory Class Req/Opt + # + # New Kokkos subpackages: + Core core PS REQUIRED + Containers containers PS OPTIONAL + Algorithms algorithms PS OPTIONAL + Example example EX OPTIONAL + ) diff --git a/lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake b/lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake new file mode 100644 index 0000000000..aad1e2bad7 --- /dev/null +++ b/lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake @@ -0,0 +1,75 @@ +# @HEADER +# ************************************************************************ +# +# Trilinos: An Object-Oriented Solver Framework +# Copyright (2001) Sandia Corporation +# +# +# Copyright (2001) Sandia Corporation. Under the terms of Contract +# DE-AC04-94AL85000, there is a non-exclusive license for use of this +# work by or on behalf of the U.S. Government. Export of this program +# may require a license from the United States Government. +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the Corporation nor the names of the +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +# 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. +# +# NOTICE: The United States Government is granted for itself and others +# acting on its behalf a paid-up, nonexclusive, irrevocable worldwide +# license in this data to reproduce, prepare derivative works, and +# perform publicly and display publicly. Beginning five (5) years from +# July 25, 2001, the United States Government is granted for itself and +# others acting on its behalf a paid-up, nonexclusive, irrevocable +# worldwide license in this data to reproduce, prepare derivative works, +# distribute copies to the public, perform publicly and display +# publicly, and to permit others to do so. +# +# NEITHER THE UNITED STATES GOVERNMENT, NOR THE UNITED STATES DEPARTMENT +# OF ENERGY, NOR SANDIA CORPORATION, NOR ANY OF THEIR EMPLOYEES, MAKES +# ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR +# RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY +# INFORMATION, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS +# THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. +# +# ************************************************************************ +# @HEADER + +# Check for CUDA support + +IF (NOT TPL_ENABLE_CUDA OR CUDA_VERSION VERSION_LESS "4.1") + MESSAGE(FATAL_ERROR "\nCUSPARSE: did not find acceptable version of CUDA libraries (4.1 or greater)") +ELSE() + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + # FindCUDA before CMake 2.8.8 does not find cusparse library; therefore, we must + find_library(CUDA_cusparse_LIBRARY + cusparse + HINTS ${CUDA_TOOLKIT_ROOT_DIR}/lib + ) + IF(CUDA_cusparse_LIBRARY STREQUAL "CUDA_cusparse_LIBRARY-NOTFOUND") + MESSAGE(FATAL_ERROR "\nCUSPARSE: could not find cuspasre library.") + ENDIF() + ENDIF(CMAKE_VERSION VERSION_LESS "2.8.8") + GLOBAL_SET(TPL_CUSPARSE_LIBRARY_DIRS) + GLOBAL_SET(TPL_CUSPARSE_INCLUDE_DIRS ${TPL_CUDA_INCLUDE_DIRS}) + GLOBAL_SET(TPL_CUSPARSE_LIBRARIES ${CUDA_cusparse_LIBRARY}) +ENDIF() + diff --git a/lib/kokkos/cmake/tpls/FindTPLHWLOC.cmake b/lib/kokkos/cmake/tpls/FindTPLHWLOC.cmake new file mode 100644 index 0000000000..715b3e9bde --- /dev/null +++ b/lib/kokkos/cmake/tpls/FindTPLHWLOC.cmake @@ -0,0 +1,71 @@ +# @HEADER +# ************************************************************************ +# +# Trilinos: An Object-Oriented Solver Framework +# Copyright (2001) Sandia Corporation +# +# +# Copyright (2001) Sandia Corporation. Under the terms of Contract +# DE-AC04-94AL85000, there is a non-exclusive license for use of this +# work by or on behalf of the U.S. Government. Export of this program +# may require a license from the United States Government. +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the Corporation nor the names of the +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +# 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. +# +# NOTICE: The United States Government is granted for itself and others +# acting on its behalf a paid-up, nonexclusive, irrevocable worldwide +# license in this data to reproduce, prepare derivative works, and +# perform publicly and display publicly. Beginning five (5) years from +# July 25, 2001, the United States Government is granted for itself and +# others acting on its behalf a paid-up, nonexclusive, irrevocable +# worldwide license in this data to reproduce, prepare derivative works, +# distribute copies to the public, perform publicly and display +# publicly, and to permit others to do so. +# +# NEITHER THE UNITED STATES GOVERNMENT, NOR THE UNITED STATES DEPARTMENT +# OF ENERGY, NOR SANDIA CORPORATION, NOR ANY OF THEIR EMPLOYEES, MAKES +# ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR +# RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY +# INFORMATION, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS +# THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. +# +# ************************************************************************ +# @HEADER + + +#----------------------------------------------------------------------------- +# Hardware locality detection and control library. +# +# Acquisition information: +# Date checked: November 2011 +# Checked by: H. Carter Edwards +# Source: http://www.open-mpi.org/projects/hwloc/ +# Version: 1.3 +# + +TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES( HWLOC + REQUIRED_HEADERS hwloc.h + REQUIRED_LIBS_NAMES "hwloc" + ) + diff --git a/lib/kokkos/cmake/tpls/FindTPLPthread.cmake b/lib/kokkos/cmake/tpls/FindTPLPthread.cmake new file mode 100644 index 0000000000..fc401d7543 --- /dev/null +++ b/lib/kokkos/cmake/tpls/FindTPLPthread.cmake @@ -0,0 +1,82 @@ +# @HEADER +# ************************************************************************ +# +# Trilinos: An Object-Oriented Solver Framework +# Copyright (2001) Sandia Corporation +# +# +# Copyright (2001) Sandia Corporation. Under the terms of Contract +# DE-AC04-94AL85000, there is a non-exclusive license for use of this +# work by or on behalf of the U.S. Government. Export of this program +# may require a license from the United States Government. +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the Corporation nor the names of the +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +# 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. +# +# NOTICE: The United States Government is granted for itself and others +# acting on its behalf a paid-up, nonexclusive, irrevocable worldwide +# license in this data to reproduce, prepare derivative works, and +# perform publicly and display publicly. Beginning five (5) years from +# July 25, 2001, the United States Government is granted for itself and +# others acting on its behalf a paid-up, nonexclusive, irrevocable +# worldwide license in this data to reproduce, prepare derivative works, +# distribute copies to the public, perform publicly and display +# publicly, and to permit others to do so. +# +# NEITHER THE UNITED STATES GOVERNMENT, NOR THE UNITED STATES DEPARTMENT +# OF ENERGY, NOR SANDIA CORPORATION, NOR ANY OF THEIR EMPLOYEES, MAKES +# ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR +# RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY +# INFORMATION, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS +# THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. +# +# ************************************************************************ +# @HEADER + + +SET(USE_THREADS FALSE) + +IF(NOT TPL_Pthread_INCLUDE_DIRS AND NOT TPL_Pthread_LIBRARY_DIRS AND NOT TPL_Pthread_LIBRARIES) + # Use CMake's Thread finder since it is a bit smarter in determining + # whether pthreads is already built into the compiler and doesn't need + # a library to link. + FIND_PACKAGE(Threads) + #If Threads found a copy of pthreads make sure it is one of the cases the tribits + #tpl system cannot handle. + IF(Threads_FOUND AND CMAKE_USE_PTHREADS_INIT) + IF(CMAKE_THREAD_LIBS_INIT STREQUAL "" OR CMAKE_THREAD_LIBS_INIT STREQUAL "-pthread") + SET(USE_THREADS TRUE) + ENDIF() + ENDIF() +ENDIF() + +IF(USE_THREADS) + SET(TPL_Pthread_INCLUDE_DIRS "") + SET(TPL_Pthread_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") + SET(TPL_Pthread_LIBRARY_DIRS "") +ELSE() + TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES( Pthread + REQUIRED_HEADERS pthread.h + REQUIRED_LIBS_NAMES pthread + ) +ENDIF() diff --git a/lib/kokkos/cmake/tpls/FindTPLQTHREAD.cmake b/lib/kokkos/cmake/tpls/FindTPLQTHREAD.cmake new file mode 100644 index 0000000000..994b72b200 --- /dev/null +++ b/lib/kokkos/cmake/tpls/FindTPLQTHREAD.cmake @@ -0,0 +1,70 @@ +# @HEADER +# ************************************************************************ +# +# Trilinos: An Object-Oriented Solver Framework +# Copyright (2001) Sandia Corporation +# +# +# Copyright (2001) Sandia Corporation. Under the terms of Contract +# DE-AC04-94AL85000, there is a non-exclusive license for use of this +# work by or on behalf of the U.S. Government. Export of this program +# may require a license from the United States Government. +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the Corporation nor the names of the +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +# 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. +# +# NOTICE: The United States Government is granted for itself and others +# acting on its behalf a paid-up, nonexclusive, irrevocable worldwide +# license in this data to reproduce, prepare derivative works, and +# perform publicly and display publicly. Beginning five (5) years from +# July 25, 2001, the United States Government is granted for itself and +# others acting on its behalf a paid-up, nonexclusive, irrevocable +# worldwide license in this data to reproduce, prepare derivative works, +# distribute copies to the public, perform publicly and display +# publicly, and to permit others to do so. +# +# NEITHER THE UNITED STATES GOVERNMENT, NOR THE UNITED STATES DEPARTMENT +# OF ENERGY, NOR SANDIA CORPORATION, NOR ANY OF THEIR EMPLOYEES, MAKES +# ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR +# RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY +# INFORMATION, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS +# THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. +# +# ************************************************************************ +# @HEADER + + +#----------------------------------------------------------------------------- +# Hardware locality detection and control library. +# +# Acquisition information: +# Date checked: July 2014 +# Checked by: H. Carter Edwards +# Source: https://code.google.com/p/qthreads +# + +TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES( QTHREAD + REQUIRED_HEADERS qthread.h + REQUIRED_LIBS_NAMES "qthread" + ) + diff --git a/lib/kokkos/config/nvcc_wrapper b/lib/kokkos/config/nvcc_wrapper index 63e0ef50a7..058911929b 100755 --- a/lib/kokkos/config/nvcc_wrapper +++ b/lib/kokkos/config/nvcc_wrapper @@ -23,36 +23,72 @@ default_arch="sm_35" # # The default C++ compiler. # -default_compiler=${NVCC_WRAPPER_DEFAULT_COMPILER:-"g++"} -#default_compiler="icpc" -#default_compiler="/usr/local/gcc/4.8.3/bin/g++" -#default_compiler="/usr/local/gcc/4.9.1/bin/g++" +host_compiler=${NVCC_WRAPPER_DEFAULT_COMPILER:-"g++"} +#host_compiler="icpc" +#host_compiler="/usr/local/gcc/4.8.3/bin/g++" +#host_compiler="/usr/local/gcc/4.9.1/bin/g++" # # Internal variables # + +# C++ files cpp_files="" + +# Host compiler arguments xcompiler_args="" -cuda_arg="" + +# Cuda (NVCC) only arguments +cuda_args="" + +# Arguments for both NVCC and Host compiler +shared_args="" + +# Linker arguments xlinker_args="" + +# Object files passable to NVCC object_files="" + +# Link objects for the host linker only object_files_xlinker="" -first_host_option=1 + +# Does the User set the architecture arch_set=0 + +# Does the user overwrite the host compiler ccbin_set=0 -nvcc_error_code=0 + +#Error code of compilation +error_code=0 + +# Do a dry run without actually compiling dry_run=0 + +# Skip NVCC compilation and use host compiler directly +host_only=0 + +# Enable workaround for CUDA 6.5 for pragma ident replace_pragma_ident=0 +# Mark first host compiler argument +first_xcompiler_arg=1 + +temp_dir=${TMPDIR:-/tmp} + #echo "Arguments: $# $@" while [ $# -gt 0 ] do case $1 in #show the executed command - --show) + --show|--nvcc-wrapper-show) dry_run=1 ;; + #run host compilation only + --host-only) + host_only=1 + ;; #replace '#pragma ident' with '#ident' this is needed to compile OpenMPI due to a configure script bug and a non standardized behaviour of pragma with macros --replace-pragma-ident) replace_pragma_ident=1 @@ -61,22 +97,31 @@ do *.cpp|*.cxx|*.cc|*.C|*.c++|*.cu) cpp_files="$cpp_files $1" ;; + #Handle shared args (valid for both nvcc and the host compiler) + -O*|-D*|-c|-I*|-L*|-l*|-g|--help|--version|-E|-M|-shared) + shared_args="$shared_args $1" + ;; + #Handle shared args that have an argument + -o) + shared_args="$shared_args $1 $2" + shift + ;; #Handle known nvcc args - -O*|-D*|-gencode*|-c|-I*|-L*|-l*|-g|--help|--version|--dryrun|--verbose|--keep-dir|-E|-M|-G|--relocatable-device-code*|-shared|-lineinfo|-expt-extended-lambda|--resource-usage) + -gencode*|--dryrun|--verbose|--keep|--keep-dir*|-G|--relocatable-device-code*|-lineinfo|-expt-extended-lambda|--resource-usage) cuda_args="$cuda_args $1" ;; + #Handle known nvcc args that have an argument + -rdc|-maxrregcount|--default-stream) + cuda_args="$cuda_args $1 $2" + shift + ;; #Handle c++11 setting --std=c++11|-std=c++11) - cuda_args="$cuda_args $1" + shared_args="$shared_args $1" ;; #strip of -std=c++98 due to nvcc warnings and Tribits will place both -std=c++11 and -std=c++98 -std=c++98|--std=c++98) ;; - #Handle known nvcc args that have an argument - -o|-rdc|-maxrregcount|--default-stream) - cuda_args="$cuda_args $1 $2" - shift - ;; #strip of pedantic because it produces endless warnings about #LINE added by the preprocessor -pedantic|-Wpedantic|-ansi) ;; @@ -86,7 +131,12 @@ do #strip of "-x cu" because we add that -x) if [[ $2 != "cu" ]]; then - xcompiler_args="$xcompiler_args,-x,$2" + if [ $first_xcompiler_arg -eq 1 ]; then + xcompiler_args="-x,$2" + first_xcompiler_arg=0 + else + xcompiler_args="$xcompiler_args,-x,$2" + fi fi shift ;; @@ -94,6 +144,7 @@ do -ccbin) cuda_args="$cuda_args $1 $2" ccbin_set=1 + host_compiler=$2 shift ;; #Handle -arch argument (if its not set use a default @@ -109,24 +160,25 @@ do #Handle args that should be sent to the linker -Wl*) xlinker_args="$xlinker_args -Xlinker ${1:4:${#1}}" + host_linker_args="$host_linker_args ${1:4:${#1}}" ;; #Handle object files: -x cu applies to all input files, so give them to linker, except if only linking *.a|*.so|*.o|*.obj) object_files="$object_files $1" object_files_xlinker="$object_files_xlinker -Xlinker $1" ;; - #Handle object files: -x cu applies to all input files, so give them to linker, except if only linking + #Handle object files which always need to use "-Xlinker": -x cu applies to all input files, so give them to linker, except if only linking *.so.*|*.dylib) - object_files_xlinker="$object_files_xlinker -Xlinker $1" object_files="$object_files -Xlinker $1" + object_files_xlinker="$object_files_xlinker -Xlinker $1" ;; #All other args are sent to the host compiler *) - if [ $first_host_option -eq 0 ]; then + if [ $first_xcompiler_arg -eq 1 ]; then + xcompiler_args=$1 + first_xcompiler_arg=0 + else xcompiler_args="$xcompiler_args,$1" - else - xcompiler_args="-Xcompiler $1" - first_host_option=0 fi ;; esac @@ -136,7 +188,7 @@ done #Add default host compiler if necessary if [ $ccbin_set -ne 1 ]; then - cuda_args="$cuda_args -ccbin $default_compiler" + cuda_args="$cuda_args -ccbin $host_compiler" fi #Add architecture command @@ -145,7 +197,13 @@ if [ $arch_set -ne 1 ]; then fi #Compose compilation command -command="nvcc $cuda_args $xlinker_args $xcompiler_args" +nvcc_command="nvcc $cuda_args $shared_args $xlinker_args" +if [ $first_xcompiler_arg -eq 0 ]; then + nvcc_command="$nvcc_command -Xcompiler $xcompiler_args" +fi + +#Compose host only command +host_command="$host_compiler $shared_args $xcompiler_args $host_linker_args" #nvcc does not accept '#pragma ident SOME_MACRO_STRING' but it does accept '#ident SOME_MACRO_STRING' if [ $replace_pragma_ident -eq 1 ]; then @@ -155,31 +213,45 @@ if [ $replace_pragma_ident -eq 1 ]; then var=`grep pragma ${file} | grep ident | grep "#"` if [ "${#var}" -gt 0 ] then - sed 's/#[\ \t]*pragma[\ \t]*ident/#ident/g' $file > /tmp/nvcc_wrapper_tmp_$file - cpp_files2="$cpp_files2 /tmp/nvcc_wrapper_tmp_$file" + sed 's/#[\ \t]*pragma[\ \t]*ident/#ident/g' $file > $temp_dir/nvcc_wrapper_tmp_$file + cpp_files2="$cpp_files2 $temp_dir/nvcc_wrapper_tmp_$file" else cpp_files2="$cpp_files2 $file" fi done cpp_files=$cpp_files2 - echo $cpp_files + #echo $cpp_files fi if [ "$cpp_files" ]; then - command="$command $object_files_xlinker -x cu $cpp_files" + nvcc_command="$nvcc_command $object_files_xlinker -x cu $cpp_files" else - command="$command $object_files" + nvcc_command="$nvcc_command $object_files" +fi + +if [ "$cpp_files" ]; then + host_command="$host_command $object_files $cpp_files" +else + host_command="$host_command $object_files" fi #Print command for dryrun if [ $dry_run -eq 1 ]; then - echo $command + if [ $host_only -eq 1 ]; then + echo $host_command + else + echo $nvcc_command + fi exit 0 fi #Run compilation command -$command -nvcc_error_code=$? +if [ $host_only -eq 1 ]; then + $host_command +else + $nvcc_command +fi +error_code=$? #Report error code -exit $nvcc_error_code +exit $error_code diff --git a/lib/kokkos/config/test_all_sandia b/lib/kokkos/config/test_all_sandia index 7d52039bea..659f14066b 100755 --- a/lib/kokkos/config/test_all_sandia +++ b/lib/kokkos/config/test_all_sandia @@ -6,8 +6,6 @@ set -o pipefail -COMPILER_ROOT="/home/projects/x86-64" - GCC_BUILD_LIST="OpenMP,Pthread,Serial,OpenMP_Serial,Pthread_Serial" INTEL_BUILD_LIST="OpenMP,Pthread,Serial,OpenMP_Serial,Pthread_Serial" CLANG_BUILD_LIST="Pthread,Serial,Pthread_Serial" @@ -18,24 +16,17 @@ CLANG_WARNING_FLAGS="-Wall,-Wshadow,-pedantic,-Werror,-Wsign-compare,-Wtype-limi INTEL_WARNING_FLAGS="-Wall,-Wshadow,-pedantic,-Werror,-Wsign-compare,-Wtype-limits,-Wuninitialized" CUDA_WARNING_FLAGS="" -# Format: (compiler module-list build-list exe-name warning-flag) -COMPILERS=("gcc/4.7.2 gcc/4.7.2/base,hwloc/1.10.0/host/gnu/4.7.2 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" - "gcc/4.8.4 gcc/4.9.2/base,hwloc/1.10.0/host/gnu/4.9.2 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" - "gcc/4.9.2 gcc/4.9.2/base,hwloc/1.10.0/host/gnu/4.9.2 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" - "gcc/5.1.0 gcc/5.1.0/base,hwloc/1.10.0/host/gnu/5.1.0 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" - "intel/14.0.4 intel/14.0.4/base,hwloc/1.10.0/host/gnu/4.7.2 $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS" - "intel/15.0.2 intel/15.0.2/base,hwloc/1.10.0/host/gnu/4.7.2 $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS" - "clang/3.5.2 clang/3.5.2/base $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS" - "clang/3.6.1 clang/3.6.1/base $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS" - "cuda/6.5.14 cuda/6.5.14,nvcc-wrapper/gnu,gcc/4.7.2/base $CUDA_BUILD_LIST nvcc_wrapper $CUDA_WARNING_FLAGS" - "cuda/7.0.28 cuda/7.0.18,nvcc-wrapper/gnu,gcc/4.7.2/base $CUDA_BUILD_LIST nvcc_wrapper $CUDA_WARNING_FLAGS" - ) +BASE_MODULE_LIST="//base,hwloc/1.10.1///base" +CUDA_MODULE_LIST="/,gcc/4.7.2/base" export OMP_NUM_THREADS=4 -export SEMS_MODULE_ROOT=/projects/modulefiles -module use /home/projects/modulefiles -module use /projects/modulefiles/rhel6-x86_64/sems/compiler +declare -i NUM_RESULTS_TO_KEEP=7 + +RESULT_ROOT_PREFIX=TestAll + +source /projects/modulefiles/utils/sems-modules-init.sh +source /projects/modulefiles/utils/kokkos-modules-init.sh SCRIPT_KOKKOS_ROOT=$( cd "$( dirname "$0" )" && cd .. && pwd ) @@ -47,6 +38,9 @@ DEBUG=False ARGS="" CUSTOM_BUILD_LIST="" DRYRUN=False +BUILD_ONLY=False +declare -i NUM_JOBS_TO_RUN_IN_PARALLEL=3 +TEST_SCRIPT=False while [[ $# > 0 ]] do @@ -61,6 +55,15 @@ CUSTOM_BUILD_LIST="${key#*=}" --debug*) DEBUG=True ;; +--build-only*) +BUILD_ONLY=True +;; +--test-script*) +TEST_SCRIPT=True +;; +--num*) +NUM_JOBS_TO_RUN_IN_PARALLEL="${key#*=}" +;; --dry-run*) DRYRUN=True ;; @@ -69,7 +72,10 @@ echo "test_all_sandia :" echo "--kokkos-path=/Path/To/Kokkos: Path to the Kokkos root directory" echo " Defaults to root repo containing this script" echo "--debug: Run tests in debug. Defaults to False" +echo "--test-script: Test this script, not Kokkos" +echo "--num=N: Number of jobs to run in parallel " echo "--dry-run: Just print what would be executed" +echo "--build-only: Just do builds, don't run anything" echo "--build-list=BUILD,BUILD,BUILD..." echo " Provide a comma-separated list of builds instead of running all builds" echo " Valid items:" @@ -77,6 +83,18 @@ echo " OpenMP, Pthread, Serial, OpenMP_Serial, Pthread_Serial" echo " Cuda_OpenMP, Cuda_Pthread, Cuda_Serial" echo "" echo "ARGS: list of expressions matching compilers to test" +echo " supported compilers" +echo " gcc/4.7.2" +echo " gcc/4.8.4" +echo " gcc/4.9.2" +echo " gcc/5.1.0" +echo " intel/14.0.4" +echo " intel/15.0.2" +echo " clang/3.5.2" +echo " clang/3.6.1" +echo " cuda/6.5.14" +echo " cuda/7.0.28" +echo " cuda/7.5.18" echo "" echo "Examples:" echo " Run all tests" @@ -93,6 +111,10 @@ echo " % test_all_sandia --debug" echo "" echo " Run gcc/4.7.2 and only do OpenMP and OpenMP_Serial builds" echo " % test_all_sandia gcc/4.7.2 --build-list=OpenMP,OpenMP_Serial" +echo "" +echo "If you want to kill the tests, do:" +echo " hit ctrl-z" +echo " % kill -9 %1" echo exit 0 ;; @@ -104,7 +126,6 @@ esac shift done - # set kokkos path if [ -z "$KOKKOS_PATH" ]; then KOKKOS_PATH=$SCRIPT_KOKKOS_ROOT @@ -125,12 +146,26 @@ if [ -z "$ARGS" ]; then ARGS='?' fi +# Format: (compiler module-list build-list exe-name warning-flag) +COMPILERS=("gcc/4.7.2 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" + "gcc/4.8.4 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" + "gcc/4.9.2 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" + "gcc/5.1.0 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS" + "intel/14.0.4 $BASE_MODULE_LIST $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS" + "intel/15.0.2 $BASE_MODULE_LIST $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS" + "clang/3.5.2 $BASE_MODULE_LIST $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS" + "clang/3.6.1 $BASE_MODULE_LIST $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS" + "cuda/6.5.14 $CUDA_MODULE_LIST $CUDA_BUILD_LIST $KOKKOS_PATH/config/nvcc_wrapper $CUDA_WARNING_FLAGS" + "cuda/7.0.28 $CUDA_MODULE_LIST $CUDA_BUILD_LIST $KOKKOS_PATH/config/nvcc_wrapper $CUDA_WARNING_FLAGS" + "cuda/7.5.18 $CUDA_MODULE_LIST $CUDA_BUILD_LIST $KOKKOS_PATH/config/nvcc_wrapper $CUDA_WARNING_FLAGS" + ) + # Process args to figure out which compilers to test COMPILERS_TO_TEST="" for ARG in $ARGS; do for COMPILER_DATA in "${COMPILERS[@]}"; do - arr=($COMPILER_DATA) - COMPILER=${arr[0]} + ARR=($COMPILER_DATA) + COMPILER=${ARR[0]} if [[ "$COMPILER" = $ARG* ]]; then if [[ "$COMPILERS_TO_TEST" != *${COMPILER}* ]]; then COMPILERS_TO_TEST="$COMPILERS_TO_TEST $COMPILER" @@ -145,15 +180,28 @@ done # Functions # +# get_compiler_name +get_compiler_name() { + echo $1 | cut -d/ -f1 +} + +# get_compiler_version +get_compiler_version() { + echo $1 | cut -d/ -f2 +} + # Do not call directly get_compiler_data() { - compiler=$1 - item=$2 + local compiler=$1 + local item=$2 + local compiler_name=$(get_compiler_name $compiler) + local compiler_vers=$(get_compiler_version $compiler) + local compiler_data for compiler_data in "${COMPILERS[@]}" ; do - arr=($compiler_data) + local arr=($compiler_data) if [ "$compiler" = "${arr[0]}" ]; then - echo "${arr[$item]}" | tr , ' ' + echo "${arr[$item]}" | tr , ' ' | sed -e "s//$compiler_name/g" -e "s//$compiler_vers/g" return 0 fi done @@ -186,33 +234,60 @@ get_compiler_warning_flags() { run_cmd() { echo "RUNNING: $*" if [ "$DRYRUN" != "True" ]; then - eval "$*" + eval "$* 2>&1" fi } +# report_and_log_test_results report_and_log_test_result() { - if [ "$1" = "0" ]; then - echo "PASSED $2" - TEST_RESULTS="${TEST_RESULTS}\nPASSED $2" + # Use sane var names + local success=$1; local desc=$2; local phase=$3; + + if [ "$success" = "0" ]; then + echo " PASSED $desc" + touch $PASSED_DIR/$desc else - echo "FAILED $2" >&2 - TEST_RESULTS="${TEST_RESULTS}\nFAILED $2 ($3)" - NUM_FAILED+=1 + echo " FAILED $desc" >&2 + echo $phase > $FAILED_DIR/$desc + cat ${desc}.${phase}.log fi } +setup_env() { + local compiler=$1 + local compiler_modules=$(get_compiler_modules $compiler) + + module purge + + local mod + for mod in $compiler_modules; do + module load $mod 2>&1 + # It is ridiculously hard to check for the success of a loaded + # module. Module does not return error codes and piping to grep + # causes module to run in a subshell. + module list 2>&1 | grep "$mod" >& /dev/null || return 1 + done + + return 0 +} + # single_build_and_test single_build_and_test() { # Use sane var names local compiler=$1; local build=$2; local build_type=$3; - cd $ROOT_DIR/$compiler + # set up env + mkdir -p $ROOT_DIR/$compiler/"${build}-$build_type" + cd $ROOT_DIR/$compiler/"${build}-$build_type" + local desc=$(echo "${compiler}-${build}-${build_type}" | sed 's:/:-:g') + setup_env $compiler >& ${desc}.configure.log || { report_and_log_test_result 1 ${desc} configure && return 0; } + # Set up flags local compiler_warning_flags=$(get_compiler_warning_flags $compiler) local compiler_exe=$(get_compiler_exe_name $compiler) if [[ "$build_type" = hwloc* ]]; then - local extra_args="--with-hwloc=$HWLOC_ROOT" + local extra_args=--with-hwloc=$(dirname $(dirname $(which hwloc-info))) fi if [[ "$build_type" = *debug* ]]; then @@ -222,36 +297,63 @@ single_build_and_test() { local cxxflags="-O3 $compiler_warning_flags" fi - local desc=$(echo "${compiler}-${build}-${build_type}" | sed 's:/:-:g') - echo " Doing build: $desc" - - mkdir "${build}-$build_type" - cd "${build}-$build_type" + if [[ "$compiler" == cuda* ]]; then + cxxflags="--keep --keep-dir=$(pwd) $cxxflags" + export TMPDIR=$(pwd) + fi # cxxflags="-DKOKKOS_USING_EXPERIMENTAL_VIEW $cxxflags" - run_cmd ${KOKKOS_PATH}/generate_makefile.bash --with-devices=$build --compiler=$(which $compiler_exe) --cxxflags=\"$cxxflags\" \"$extra_args\" 2>&1 | tee ${desc}.configure.log || { report_and_log_test_result 1 ${desc} configure && return 0; } - run_cmd make build-test 2>&1 | tee ${desc}.build.log || { report_and_log_test_result 1 ${desc} build && return 0; } - run_cmd make test 2>&1 | tee ${desc}.test.log || { report_and_log_test_result 1 ${desc} test && return 0; } + echo " Starting job $desc" + + if [ "$TEST_SCRIPT" = "True" ]; then + local rand=$[ 1 + $[ RANDOM % 10 ]] + sleep $rand + if [ $rand -gt 5 ]; then + run_cmd ls fake_problem >& ${desc}.configure.log || { report_and_log_test_result 1 $desc configure && return 0; } + fi + else + run_cmd ${KOKKOS_PATH}/generate_makefile.bash --with-devices=$build --compiler=$(which $compiler_exe) --cxxflags=\"$cxxflags\" $extra_args >& ${desc}.configure.log || { report_and_log_test_result 1 ${desc} configure && return 0; } + run_cmd make build-test >& ${desc}.build.log || { report_and_log_test_result 1 ${desc} build && return 0; } + if [[ "$BUILD_ONLY" == False ]]; then + run_cmd make test >& ${desc}.test.log || { report_and_log_test_result 1 ${desc} test && return 0; } + fi + fi + report_and_log_test_result 0 $desc + return 0 } -setup_env() { - local compiler=$1 - local compiler_modules=$(get_compiler_modules $compiler) - - module purge - - for mod in $compiler_modules; do - module load $mod - # It is ridiculously hard to check for the success of a loaded - # module. Module does not return error codes and piping to grep - # causes module to run in a subshell. - module list 2>&1 | grep "$mod" +# wait_for_jobs +wait_for_jobs() { + local -i max_jobs=$1 + local -i num_active_jobs=$(jobs | wc -l) + while [ $num_active_jobs -ge $max_jobs ] + do + sleep 1 + num_active_jobs=$(jobs | wc -l) + jobs >& /dev/null done } +# run_in_background +run_in_background() { + local compiler=$1 + + local -i num_jobs=$NUM_JOBS_TO_RUN_IN_PARALLEL + if [[ "$BUILD_ONLY" == True ]]; then + num_jobs=8 + else + if [[ "$compiler" == cuda* ]]; then + num_jobs=1 + fi + fi + wait_for_jobs $num_jobs + + single_build_and_test $* & +} + # build_and_test_all build_and_test_all() { # Get compiler data @@ -262,44 +364,74 @@ build_and_test_all() { local compiler_build_list=$(echo "$CUSTOM_BUILD_LIST" | tr , ' ') fi - # set up env - cd $ROOT_DIR - mkdir -p $compiler - setup_env $compiler - # do builds + local build for build in $compiler_build_list do - single_build_and_test $compiler $build $BUILD_TYPE + run_in_background $compiler $build $BUILD_TYPE # If not cuda, do a hwloc test too if [[ "$compiler" != cuda* ]]; then - single_build_and_test $compiler $build "hwloc-$BUILD_TYPE" + run_in_background $compiler $build "hwloc-$BUILD_TYPE" fi done return 0 } +get_test_root_dir() { + local existing_results=$(find . -maxdepth 1 -name "$RESULT_ROOT_PREFIX*" | sort) + local -i num_existing_results=$(echo $existing_results | tr ' ' '\n' | wc -l) + local -i num_to_delete=${num_existing_results}-${NUM_RESULTS_TO_KEEP} + + if [ $num_to_delete -gt 0 ]; then + /bin/rm -rf $(echo $existing_results | tr ' ' '\n' | head -n $num_to_delete) + fi + + echo $(pwd)/${RESULT_ROOT_PREFIX}_$(date +"%Y-%m-%d_%H.%M.%S") +} + +wait_summarize_and_exit() { + wait_for_jobs 1 + + echo "#######################################################" + echo "PASSED TESTS" + echo "#######################################################" + + \ls -1 $PASSED_DIR | sort + + echo "#######################################################" + echo "FAILED TESTS" + echo "#######################################################" + + local failed_test + local -i rv=0 + for failed_test in $(\ls -1 $FAILED_DIR) + do + echo $failed_test "("$(cat $FAILED_DIR/$failed_test)" failed)" + rv=$rv+1 + done + + exit $rv +} + # # Main # -/bin/rm -rf TestAll -mkdir TestAll -cd TestAll +ROOT_DIR=$(get_test_root_dir) +mkdir -p $ROOT_DIR +cd $ROOT_DIR -TEST_RESULTS="" -declare -i NUM_FAILED=0 -ROOT_DIR=$(pwd) +PASSED_DIR=$ROOT_DIR/results/passed +FAILED_DIR=$ROOT_DIR/results/failed +mkdir -p $PASSED_DIR +mkdir -p $FAILED_DIR + +echo "Going to test compilers: " $COMPILERS_TO_TEST for COMPILER in $COMPILERS_TO_TEST; do echo "Testing compiler $COMPILER" build_and_test_all $COMPILER done -echo "#######################################################" -echo "RESULT SUMMARY" -echo "#######################################################" -echo -e $TEST_RESULTS - -exit $NUM_FAILED +wait_summarize_and_exit diff --git a/lib/kokkos/config/testing_scripts/obj_size_opt_check b/lib/kokkos/config/testing_scripts/obj_size_opt_check new file mode 100755 index 0000000000..47c84d1a92 --- /dev/null +++ b/lib/kokkos/config/testing_scripts/obj_size_opt_check @@ -0,0 +1,287 @@ +#! /usr/bin/env python + +""" +Compute the size at which the current compiler will start to +significantly scale back optimization. + +The CPP file being modified will need the following tags. +// JGF_DUPLICATE_BEGIN - Put before start of function to duplicate +// JGF_DUPLICATE_END - Put after end of function to duplcate +// JGF_DUPE function_name(args); - Put anywhere where it's legal to +put a function call but not in your timing section. + +The program will need to output the string: +FOM: +This will represent the program's performance +""" + +import argparse, sys, os, doctest, subprocess, re, time + +VERBOSE = False + +############################################################################### +def parse_command_line(args, description): +############################################################################### + parser = argparse.ArgumentParser( + usage="""\n%s [--verbose] +OR +%s --help +OR +%s --test + +\033[1mEXAMPLES:\033[0m + > %s foo.cpp 'make -j4' foo +""" % ((os.path.basename(args[0]), ) * 4), + +description=description, + +formatter_class=argparse.ArgumentDefaultsHelpFormatter +) + + parser.add_argument("cppfile", help="Name of file to modify.") + + parser.add_argument("buildcmd", help="Build command") + + parser.add_argument("execmd", help="Run command") + + parser.add_argument("-v", "--verbose", action="store_true", + help="Print extra information") + + parser.add_argument("-s", "--start", type=int, default=1, + help="Starting number of dupes") + + parser.add_argument("-e", "--end", type=int, default=1000, + help="Ending number of dupes") + + parser.add_argument("-n", "--repeat", type=int, default=10, + help="Number of times to repeat an individial execution. Best value will be taken.") + + parser.add_argument("-t", "--template", action="store_true", + help="Use templating instead of source copying to increase object size") + + parser.add_argument("-c", "--csv", action="store_true", + help="Print results as CSV") + + args = parser.parse_args(args[1:]) + + if (args.verbose): + global VERBOSE + VERBOSE = True + + return args.cppfile, args.buildcmd, args.execmd, args.start, args.end, args.repeat, args.template, args.csv + +############################################################################### +def verbose_print(msg, override=None): +############################################################################### + if ( (VERBOSE and not override is False) or override): + print msg + +############################################################################### +def error_print(msg): +############################################################################### + print >> sys.stderr, msg + +############################################################################### +def expect(condition, error_msg): +############################################################################### + """ + Similar to assert except doesn't generate an ugly stacktrace. Useful for + checking user error, not programming error. + """ + if (not condition): + raise SystemExit("FAIL: %s" % error_msg) + +############################################################################### +def run_cmd(cmd, ok_to_fail=False, input_str=None, from_dir=None, verbose=None, + arg_stdout=subprocess.PIPE, arg_stderr=subprocess.PIPE): +############################################################################### + verbose_print("RUN: %s" % cmd, verbose) + + if (input_str is not None): + stdin = subprocess.PIPE + else: + stdin = None + + proc = subprocess.Popen(cmd, + shell=True, + stdout=arg_stdout, + stderr=arg_stderr, + stdin=stdin, + cwd=from_dir) + output, errput = proc.communicate(input_str) + output = output.strip() if output is not None else output + stat = proc.wait() + + if (ok_to_fail): + return stat, output, errput + else: + if (arg_stderr is not None): + errput = errput if errput is not None else open(arg_stderr.name, "r").read() + expect(stat == 0, "Command: '%s' failed with error '%s'" % (cmd, errput)) + else: + expect(stat == 0, "Command: '%s' failed. See terminal output" % cmd) + return output + +############################################################################### +def build_and_run(source, cppfile, buildcmd, execmd, repeat): +############################################################################### + open(cppfile, 'w').writelines(source) + + run_cmd(buildcmd) + + best = None + for i in xrange(repeat): + wait_for_quiet_machine() + output = run_cmd(execmd) + + current = None + fom_regex = re.compile(r'^FOM: ([0-9.]+)$') + for line in output.splitlines(): + m = fom_regex.match(line) + if (m is not None): + current = float(m.groups()[0]) + break + + expect(current is not None, "No lines in output matched FOM regex") + + if (best is None or best < current): + best = current + + return best + +############################################################################### +def wait_for_quiet_machine(): +############################################################################### + while(True): + time.sleep(2) + + # The first iteration of top gives garbage results + idle_pct_raw = run_cmd("top -bn2 | grep 'Cpu(s)' | tr ',' ' ' | tail -n 1 | awk '{print $5}'") + + idle_pct_re = re.compile(r'^([0-9.]+)%id$') + m = idle_pct_re.match(idle_pct_raw) + + expect(m is not None, "top not returning output in expected form") + + idle_pct = float(m.groups()[0]) + if (idle_pct < 95): + error_print("Machine is too busy, waiting for it to become free") + else: + break + +############################################################################### +def add_n_dupes(curr_lines, num_dupes, template): +############################################################################### + function_name = None + function_invocation = None + function_lines = [] + + function_re = re.compile(r'^.* (\w+) *[(]') + function_inv_re = re.compile(r'^.*JGF_DUPE: +(.+)$') + + # Get function lines + record = False + definition_insertion_point = None + invocation_insertion_point = None + for idx, line in enumerate(curr_lines): + if ("JGF_DUPLICATE_BEGIN" in line): + record = True + m = function_re.match(curr_lines[idx+1]) + expect(m is not None, "Could not find function in line '%s'" % curr_lines[idx+1]) + function_name = m.groups()[0] + + elif ("JGF_DUPLICATE_END" in line): + record = False + definition_insertion_point = idx + 1 + + elif (record): + function_lines.append(line) + + elif ("JGF_DUPE" in line): + m = function_inv_re.match(line) + expect(m is not None, "Could not find function invocation example in line '%s'" % line) + function_invocation = m.groups()[0] + invocation_insertion_point = idx + 1 + + expect(function_name is not None, "Could not find name of dupe function") + expect(function_invocation is not None, "Could not find function invocation point") + + expect(definition_insertion_point < invocation_insertion_point, "fix me") + + dupe_func_defs = [] + dupe_invocations = ["int jgf_rand = std::rand();\n", "if (false) {}\n"] + + for i in xrange(num_dupes): + if (not template): + dupe_func = list(function_lines) + dupe_func[0] = dupe_func[0].replace(function_name, "%s%d" % (function_name, i)) + dupe_func_defs.extend(dupe_func) + + dupe_invocations.append("else if (jgf_rand == %d) " % i) + if (template): + dupe_call = function_invocation.replace(function_name, "%s<%d>" % (function_name, i)) + "\n" + else: + dupe_call = function_invocation.replace(function_name, "%s%d" % (function_name, i)) + "\n" + dupe_invocations.append(dupe_call) + + curr_lines[invocation_insertion_point:invocation_insertion_point] = dupe_invocations + curr_lines[definition_insertion_point:definition_insertion_point] = dupe_func_defs + +############################################################################### +def report(num_dupes, curr_lines, object_file, orig_fom, curr_fom, csv=False, is_first_report=False): +############################################################################### + fom_change = (curr_fom - orig_fom) / orig_fom + + if (csv): + if (is_first_report): + print "num_dupes, obj_byte_size, loc, fom, pct_diff" + + print "%s, %s, %s, %s, %s" % (num_dupes, os.path.getsize(object_file), len(curr_lines), curr_fom, fom_change*100) + else: + print "========================================================" + print "For number of dupes:", num_dupes + print "Object file size (bytes):", os.path.getsize(object_file) + print "Lines of code:", len(curr_lines) + print "Field of merit:", curr_fom + print "Change pct:", fom_change*100 + +############################################################################### +def obj_size_opt_check(cppfile, buildcmd, execmd, start, end, repeat, template, csv=False): +############################################################################### + orig_source_lines = open(cppfile, 'r').readlines() + + backup_file = "%s.orig" % cppfile + object_file = "%s.o" % os.path.splitext(cppfile)[0] + os.rename(cppfile, backup_file) + + orig_fom = build_and_run(orig_source_lines, cppfile, buildcmd, execmd, repeat) + report(0, orig_source_lines, object_file, orig_fom, orig_fom, csv=csv, is_first_report=True) + + i = start + while (i < end): + curr_lines = list(orig_source_lines) + add_n_dupes(curr_lines, i, template) + + curr_fom = build_and_run(curr_lines, cppfile, buildcmd, execmd, repeat) + + report(i, curr_lines, object_file, orig_fom, curr_fom, csv=csv) + + i *= 2 # make growth function configurable? + + os.remove(cppfile) + os.rename(backup_file, cppfile) + +############################################################################### +def _main_func(description): +############################################################################### + if ("--test" in sys.argv): + test_results = doctest.testmod(verbose=True) + sys.exit(1 if test_results.failed > 0 else 0) + + cppfile, buildcmd, execmd, start, end, repeat, template, csv = parse_command_line(sys.argv, description) + + obj_size_opt_check(cppfile, buildcmd, execmd, start, end, repeat, template, csv) + +############################################################################### +if (__name__ == "__main__"): + _main_func(__doc__) diff --git a/lib/kokkos/containers/CMakeLists.txt b/lib/kokkos/containers/CMakeLists.txt new file mode 100644 index 0000000000..894935fa01 --- /dev/null +++ b/lib/kokkos/containers/CMakeLists.txt @@ -0,0 +1,10 @@ + + +TRIBITS_SUBPACKAGE(Containers) + +ADD_SUBDIRECTORY(src) + +TRIBITS_ADD_TEST_DIRECTORIES(unit_tests) +TRIBITS_ADD_TEST_DIRECTORIES(performance_tests) + +TRIBITS_SUBPACKAGE_POSTPROCESS() diff --git a/lib/kokkos/containers/cmake/Dependencies.cmake b/lib/kokkos/containers/cmake/Dependencies.cmake new file mode 100644 index 0000000000..1d71d8af34 --- /dev/null +++ b/lib/kokkos/containers/cmake/Dependencies.cmake @@ -0,0 +1,5 @@ +TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( + LIB_REQUIRED_PACKAGES KokkosCore + LIB_OPTIONAL_TPLS Pthread CUDA HWLOC + TEST_OPTIONAL_TPLS CUSPARSE + ) diff --git a/lib/kokkos/containers/cmake/KokkosContainers_config.h.in b/lib/kokkos/containers/cmake/KokkosContainers_config.h.in new file mode 100644 index 0000000000..d91fdda1e3 --- /dev/null +++ b/lib/kokkos/containers/cmake/KokkosContainers_config.h.in @@ -0,0 +1,4 @@ +#ifndef KOKKOS_CONTAINERS_CONFIG_H +#define KOKKOS_CONTAINERS_CONFIG_H + +#endif diff --git a/lib/kokkos/containers/performance_tests/CMakeLists.txt b/lib/kokkos/containers/performance_tests/CMakeLists.txt new file mode 100644 index 0000000000..6b57802935 --- /dev/null +++ b/lib/kokkos/containers/performance_tests/CMakeLists.txt @@ -0,0 +1,26 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../src ) + +SET(SOURCES + TestMain.cpp + TestCuda.cpp + ) + +IF(Kokkos_ENABLE_Pthread) + LIST( APPEND SOURCES TestThreads.cpp) +ENDIF() + +IF(Kokkos_ENABLE_OpenMP) + LIST( APPEND SOURCES TestOpenMP.cpp) +ENDIF() + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + PerformanceTest + SOURCES ${SOURCES} + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) diff --git a/lib/kokkos/containers/performance_tests/Makefile b/lib/kokkos/containers/performance_tests/Makefile index 7ced945282..e7abaf44ce 100644 --- a/lib/kokkos/containers/performance_tests/Makefile +++ b/lib/kokkos/containers/performance_tests/Makefile @@ -6,12 +6,12 @@ vpath %.cpp ${KOKKOS_PATH}/containers/performance_tests default: build_all echo "End Build" - + include $(KOKKOS_PATH)/Makefile.kokkos ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) - CXX = nvcc_wrapper + CXX = $(NVCC_WRAPPER) CXXFLAGS ?= -O3 LINK = $(CXX) LDFLAGS ?= -lpthread @@ -50,7 +50,7 @@ KokkosContainers_PerformanceTest_Cuda: $(OBJ_CUDA) $(KOKKOS_LINK_DEPENDS) KokkosContainers_PerformanceTest_Threads: $(OBJ_THREADS) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_THREADS) $(KOKKOS_LIBS) $(LIB) -o KokkosContainers_PerformanceTest_Threads - + KokkosContainers_PerformanceTest_OpenMP: $(OBJ_OPENMP) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_OPENMP) $(KOKKOS_LIBS) $(LIB) -o KokkosContainers_PerformanceTest_OpenMP @@ -63,11 +63,11 @@ test-threads: KokkosContainers_PerformanceTest_Threads test-openmp: KokkosContainers_PerformanceTest_OpenMP ./KokkosContainers_PerformanceTest_OpenMP - + build_all: $(TARGETS) test: $(TEST_TARGETS) - + clean: kokkos-clean rm -f *.o $(TARGETS) diff --git a/lib/kokkos/containers/src/CMakeLists.txt b/lib/kokkos/containers/src/CMakeLists.txt new file mode 100644 index 0000000000..da5a791530 --- /dev/null +++ b/lib/kokkos/containers/src/CMakeLists.txt @@ -0,0 +1,31 @@ + +TRIBITS_CONFIGURE_FILE(${PACKAGE_NAME}_config.h) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +#----------------------------------------------------------------------------- + +SET(HEADERS "") +SET(SOURCES "") + +SET(HEADERS_IMPL "") + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB HEADERS_IMPL impl/*.hpp) +FILE(GLOB SOURCES impl/*.cpp) + +SET(TRILINOS_INCDIR ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}) + +INSTALL(FILES ${HEADERS_IMPL} DESTINATION ${TRILINOS_INCDIR}/impl/) + +TRIBITS_ADD_LIBRARY( + kokkoscontainers + HEADERS ${HEADERS} + NOINSTALLHEADERS ${HEADERS_IMPL} + SOURCES ${SOURCES} + DEPLIBS + ) + +#----------------------------------------------------------------------------- + diff --git a/lib/kokkos/containers/src/Kokkos_Bitset.hpp b/lib/kokkos/containers/src/Kokkos_Bitset.hpp index b51b1c2b26..74da5f61b5 100644 --- a/lib/kokkos/containers/src/Kokkos_Bitset.hpp +++ b/lib/kokkos/containers/src/Kokkos_Bitset.hpp @@ -90,7 +90,7 @@ public: private: enum { block_size = static_cast(sizeof(unsigned)*CHAR_BIT) }; enum { block_mask = block_size-1u }; - enum { block_shift = static_cast(Impl::power_of_two::value) }; + enum { block_shift = Kokkos::Impl::integral_power_of_two(block_size) }; public: @@ -322,7 +322,7 @@ public: private: enum { block_size = static_cast(sizeof(unsigned)*CHAR_BIT) }; enum { block_mask = block_size -1u }; - enum { block_shift = static_cast(Impl::power_of_two::value) }; + enum { block_shift = Kokkos::Impl::integral_power_of_two(block_size) }; public: ConstBitset() diff --git a/lib/kokkos/containers/src/Kokkos_DualView.hpp b/lib/kokkos/containers/src/Kokkos_DualView.hpp index 5e70731bdc..68d033641b 100644 --- a/lib/kokkos/containers/src/Kokkos_DualView.hpp +++ b/lib/kokkos/containers/src/Kokkos_DualView.hpp @@ -106,9 +106,9 @@ public: //! The type of a Kokkos::View on the device. typedef View< typename traits::data_type , - typename traits::array_layout , - typename traits::device_type , - typename traits::memory_traits > t_dev ; + Arg1Type , + Arg2Type , + Arg3Type > t_dev ; /// \typedef t_host /// \brief The type of a Kokkos::View host mirror of \c t_dev. @@ -117,9 +117,9 @@ public: //! The type of a const View on the device. //! The type of a Kokkos::View on the device. typedef View< typename traits::const_data_type , - typename traits::array_layout , - typename traits::device_type , - typename traits::memory_traits > t_dev_const ; + Arg1Type , + Arg2Type , + Arg3Type > t_dev_const ; /// \typedef t_host_const /// \brief The type of a const View host mirror of \c t_dev_const. @@ -221,6 +221,19 @@ public: modified_host (src.modified_host) {} + //! Subview constructor + template< class SD, class S1 , class S2 , class S3 + , class Arg0 , class ... Args > + DualView( const DualView & src + , const Arg0 & arg0 + , Args ... args + ) + : d_view( Kokkos::subview( src.d_view , arg0 , args ... ) ) + , h_view( Kokkos::subview( src.h_view , arg0 , args ... ) ) + , modified_device (src.modified_device) + , modified_host (src.modified_host) + {} + /// \brief Create DualView from existing device and host View objects. /// /// This constructor assumes that the device and host View objects @@ -237,7 +250,30 @@ public: modified_device (View ("DualView::modified_device")), modified_host (View ("DualView::modified_host")) { +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) Impl::assert_shapes_are_equal (d_view.shape (), h_view.shape ()); +#else + if ( d_view.rank != h_view.rank || + d_view.dimension_0() != h_view.dimension_0() || + d_view.dimension_1() != h_view.dimension_1() || + d_view.dimension_2() != h_view.dimension_2() || + d_view.dimension_3() != h_view.dimension_3() || + d_view.dimension_4() != h_view.dimension_4() || + d_view.dimension_5() != h_view.dimension_5() || + d_view.dimension_6() != h_view.dimension_6() || + d_view.dimension_7() != h_view.dimension_7() || + d_view.stride_0() != h_view.stride_0() || + d_view.stride_1() != h_view.stride_1() || + d_view.stride_2() != h_view.stride_2() || + d_view.stride_3() != h_view.stride_3() || + d_view.stride_4() != h_view.stride_4() || + d_view.stride_5() != h_view.stride_5() || + d_view.stride_6() != h_view.stride_6() || + d_view.stride_7() != h_view.stride_7() || + d_view.span() != h_view.span() ) { + Kokkos::Impl::throw_runtime_exception("DualView constructed with incompatible views"); + } +#endif } //@} @@ -501,6 +537,52 @@ public: }; } // namespace Kokkos + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +// +// Partial specializations of Kokkos::subview() for DualView objects. +// + +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + +namespace Kokkos { +namespace Impl { + +template< class D, class A1, class A2, class A3, class ... Args > +struct DualViewSubview { + + typedef typename Kokkos::Experimental::Impl::ViewMapping + < void + , Kokkos::ViewTraits< D, A1, A2, A3 > + , Args ... + >::traits_type dst_traits ; + + typedef Kokkos::DualView + < typename dst_traits::data_type + , typename dst_traits::array_layout + , typename dst_traits::device_type + , typename dst_traits::memory_traits + > type ; +}; + +} /* namespace Impl */ + + +template< class D , class A1 , class A2 , class A3 , class ... Args > +typename Impl::DualViewSubview::type +subview( const DualView & src , Args ... args ) +{ + return typename + Impl::DualViewSubview::type( src , args ... ); +} + +} /* namespace Kokkos */ + +#else + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- // // Partial specializations of Kokkos::subview() for DualView objects. // @@ -839,6 +921,15 @@ subview( const DualView & src , return sub_view; } +} // namespace Kokkos + +#endif /* defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { + // // Partial specialization of Kokkos::deep_copy() for DualView objects. // diff --git a/lib/kokkos/containers/src/Kokkos_Vector.hpp b/lib/kokkos/containers/src/Kokkos_Vector.hpp index db54b0c350..6a360e8d19 100644 --- a/lib/kokkos/containers/src/Kokkos_Vector.hpp +++ b/lib/kokkos/containers/src/Kokkos_Vector.hpp @@ -53,12 +53,8 @@ */ namespace Kokkos { -template -class vector : public DualView { -public: - typedef typename Space::memory_space memory_space; - typedef typename Space::execution_space execution_space; - typedef typename Kokkos::Device device_type; +template< class Scalar, class Arg1Type = void> +class vector : public DualView { typedef Scalar value_type; typedef Scalar* pointer; @@ -72,7 +68,7 @@ private: size_t _size; typedef size_t size_type; float _extra_storage; - typedef DualView DV; + typedef DualView DV; public: @@ -93,7 +89,7 @@ public: }; - vector(int n, Scalar val=Scalar()):DualView("Vector",size_t(n*(1.1))) { + vector(int n, Scalar val=Scalar()):DualView("Vector",size_t(n*(1.1))) { _size = n; _extra_storage = 1.1; DV::modified_host() = 1; diff --git a/lib/kokkos/containers/unit_tests/CMakeLists.txt b/lib/kokkos/containers/unit_tests/CMakeLists.txt new file mode 100644 index 0000000000..7fff0f835b --- /dev/null +++ b/lib/kokkos/containers/unit_tests/CMakeLists.txt @@ -0,0 +1,40 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../src ) + +SET(SOURCES + UnitTestMain.cpp + TestCuda.cpp + ) + +SET(LIBRARIES kokkoscore) + +IF(Kokkos_ENABLE_Pthread) + LIST( APPEND SOURCES + TestThreads.cpp + ) +ENDIF() + +IF(Kokkos_ENABLE_Serial) + LIST( APPEND SOURCES + TestSerial.cpp + ) +ENDIF() + +IF(Kokkos_ENABLE_OpenMP) + LIST( APPEND SOURCES + TestOpenMP.cpp + ) +ENDIF() + + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest + SOURCES ${SOURCES} + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) + diff --git a/lib/kokkos/containers/unit_tests/Makefile b/lib/kokkos/containers/unit_tests/Makefile index 176bfa906e..48e3ff61d0 100644 --- a/lib/kokkos/containers/unit_tests/Makefile +++ b/lib/kokkos/containers/unit_tests/Makefile @@ -6,12 +6,12 @@ vpath %.cpp ${KOKKOS_PATH}/containers/unit_tests default: build_all echo "End Build" - + include $(KOKKOS_PATH)/Makefile.kokkos ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) - CXX = nvcc_wrapper + CXX = $(NVCC_WRAPPER) CXXFLAGS ?= -O3 LINK = $(CXX) LDFLAGS ?= -lpthread @@ -56,7 +56,7 @@ KokkosContainers_UnitTest_Cuda: $(OBJ_CUDA) $(KOKKOS_LINK_DEPENDS) KokkosContainers_UnitTest_Threads: $(OBJ_THREADS) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_THREADS) $(KOKKOS_LIBS) $(LIB) -o KokkosContainers_UnitTest_Threads - + KokkosContainers_UnitTest_OpenMP: $(OBJ_OPENMP) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_OPENMP) $(KOKKOS_LIBS) $(LIB) -o KokkosContainers_UnitTest_OpenMP @@ -74,11 +74,11 @@ test-openmp: KokkosContainers_UnitTest_OpenMP test-serial: KokkosContainers_UnitTest_Serial ./KokkosContainers_UnitTest_Serial - + build_all: $(TARGETS) test: $(TEST_TARGETS) - + clean: kokkos-clean rm -f *.o $(TARGETS) diff --git a/lib/kokkos/containers/unit_tests/TestComplex.hpp b/lib/kokkos/containers/unit_tests/TestComplex.hpp index 5065d72579..94c04b61f4 100644 --- a/lib/kokkos/containers/unit_tests/TestComplex.hpp +++ b/lib/kokkos/containers/unit_tests/TestComplex.hpp @@ -1,12 +1,12 @@ //@HEADER // ************************************************************************ -// +// // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation -// +// // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -35,7 +35,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) -// +// // ************************************************************************ //@HEADER @@ -43,7 +43,7 @@ #ifndef KOKKOS_TEST_COMPLEX_HPP #define KOKKOS_TEST_COMPLEX_HPP -//#include +#include #include #include @@ -124,14 +124,13 @@ namespace Impl { complex_type z1 (1.0, -1.0); complex_type z2 (-1.0, 1.0); - complex_type z3 = z1 - z2; - ASSERT_TRUE( z3 == complex_type (2.0, -2.0) ); + complex_type z3 = z1 * z2; + ASSERT_TRUE( z3 == complex_type (0.0, 2.0) ); - // Test unary minus. - complex_type z4 (3.0, -4.0); - ASSERT_TRUE( z4 == complex_type (3.0, -4.0) ); - ASSERT_TRUE( -z4 == complex_type (-3.0, 4.0) ); - ASSERT_TRUE( z4 == -complex_type (-3.0, 4.0) ); + // Make sure that std::complex * Kokkos::complex works too. + std::complex z4 (-1.0, 1.0); + complex_type z5 = z4 * z1; + ASSERT_TRUE( z5 == complex_type (0.0, 2.0) ); } template @@ -208,7 +207,7 @@ namespace Impl { typedef Kokkos::View*, Device> view_type; typedef typename view_type::size_type size_type; - typedef Kokkos::complex value_type; + typedef Kokkos::complex value_type; KOKKOS_INLINE_FUNCTION void operator () (const size_type i, Kokkos::complex& sum) const { diff --git a/lib/kokkos/core/CMakeLists.txt b/lib/kokkos/core/CMakeLists.txt new file mode 100644 index 0000000000..42fce6b2f2 --- /dev/null +++ b/lib/kokkos/core/CMakeLists.txt @@ -0,0 +1,11 @@ + + +TRIBITS_SUBPACKAGE(Core) + +ADD_SUBDIRECTORY(src) + +TRIBITS_ADD_TEST_DIRECTORIES(unit_test) +TRIBITS_ADD_TEST_DIRECTORIES(perf_test) + +TRIBITS_SUBPACKAGE_POSTPROCESS() + diff --git a/lib/kokkos/core/cmake/Dependencies.cmake b/lib/kokkos/core/cmake/Dependencies.cmake new file mode 100644 index 0000000000..13ade23a9c --- /dev/null +++ b/lib/kokkos/core/cmake/Dependencies.cmake @@ -0,0 +1,4 @@ +TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( + LIB_OPTIONAL_TPLS Pthread CUDA HWLOC QTHREAD + TEST_OPTIONAL_TPLS CUSPARSE + ) diff --git a/lib/kokkos/core/cmake/KokkosCore_config.h.in b/lib/kokkos/core/cmake/KokkosCore_config.h.in new file mode 100644 index 0000000000..d381c59a2d --- /dev/null +++ b/lib/kokkos/core/cmake/KokkosCore_config.h.in @@ -0,0 +1,50 @@ +#ifndef KOKKOS_CORE_CONFIG_H +#define KOKKOS_CORE_CONFIG_H + +/* The trivial 'src/build_common.sh' creates a config + * that must stay in sync with this file. + */ +#cmakedefine KOKKOS_FOR_SIERRA + +#if !defined( KOKKOS_FOR_SIERRA ) + +#cmakedefine KOKKOS_HAVE_MPI +#cmakedefine KOKKOS_HAVE_CUDA + +// mfh 16 Sep 2014: If passed in on the command line, that overrides +// any value of KOKKOS_USE_CUDA_UVM here. Doing this should prevent build +// warnings like this one: +// +// packages/kokkos/core/src/KokkosCore_config.h:13:1: warning: "KOKKOS_USE_CUDA_UVM" redefined +// +// At some point, we should edit the test-build scripts in +// Trilinos/cmake/ctest/drivers/perseus/, and take +// -DKOKKOS_USE_CUDA_UVM from the command-line arguments there. I +// hesitate to do that now, because I'm not sure if all the files are +// including KokkosCore_config.h (or a header file that includes it) like +// they should. + +#if ! defined(KOKKOS_USE_CUDA_UVM) +#cmakedefine KOKKOS_USE_CUDA_UVM +#endif // ! defined(KOKKOS_USE_CUDA_UVM) + +#cmakedefine KOKKOS_HAVE_PTHREAD +#cmakedefine KOKKOS_HAVE_SERIAL +#cmakedefine KOKKOS_HAVE_QTHREAD +#cmakedefine KOKKOS_HAVE_Winthread +#cmakedefine KOKKOS_HAVE_OPENMP +#cmakedefine KOKKOS_HAVE_HWLOC +#cmakedefine KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK +#cmakedefine KOKKOS_HAVE_CXX11 +#cmakedefine KOKKOS_HAVE_CUSPARSE +#cmakedefine KOKKOS_ENABLE_PROFILING_COLLECT_KERNEL_DATA +#cmakedefine KOKKOS_ENABLE_PROFILING_AGGREGATE_MPI + +// Don't forbid users from defining this macro on the command line, +// but still make sure that CMake logic can control its definition. +#if ! defined(KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA) +#cmakedefine KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA 1 +#endif // KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA + +#endif // KOKKOS_FOR_SIERRA +#endif // KOKKOS_CORE_CONFIG_H diff --git a/lib/kokkos/core/perf_test/CMakeLists.txt b/lib/kokkos/core/perf_test/CMakeLists.txt new file mode 100644 index 0000000000..34aa81e92c --- /dev/null +++ b/lib/kokkos/core/perf_test/CMakeLists.txt @@ -0,0 +1,18 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINRARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES + PerfTestMain.cpp + PerfTestHost.cpp + PerfTestCuda.cpp + ) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + PerfTest + SOURCES ${SOURCES} + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) diff --git a/lib/kokkos/core/perf_test/Makefile b/lib/kokkos/core/perf_test/Makefile index 2bf189a22f..8fa1fbfc3c 100644 --- a/lib/kokkos/core/perf_test/Makefile +++ b/lib/kokkos/core/perf_test/Makefile @@ -1,17 +1,17 @@ KOKKOS_PATH = ../.. -GTEST_PATH = ../../TPL/gtest +GTEST_PATH = ../../tpls/gtest vpath %.cpp ${KOKKOS_PATH}/core/perf_test default: build_all echo "End Build" - + include $(KOKKOS_PATH)/Makefile.kokkos ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) - CXX = nvcc_wrapper + CXX = $(NVCC_WRAPPER) CXXFLAGS ?= -O3 LINK = $(CXX) LDFLAGS ?= -lpthread @@ -47,12 +47,12 @@ test-performance: KokkosCore_PerformanceTest test-atomic: KokkosCore_PerformanceTest_Atomics ./KokkosCore_PerformanceTest_Atomics - + build_all: $(TARGETS) test: $(TEST_TARGETS) - + clean: kokkos-clean rm -f *.o $(TARGETS) diff --git a/lib/kokkos/core/perf_test/PerfTestCuda.cpp b/lib/kokkos/core/perf_test/PerfTestCuda.cpp index 1263a7672a..4a4bc13cd4 100644 --- a/lib/kokkos/core/perf_test/PerfTestCuda.cpp +++ b/lib/kokkos/core/perf_test/PerfTestCuda.cpp @@ -174,7 +174,7 @@ struct TextureFetch TEST_F( cuda, texture_double ) { printf("Random reduce of double through texture fetch\n"); - for (int i=1; i<=27; ++i) { + for (int i=1; i<=26; ++i) { int size = 1< +struct ViewOperatorBoundsErrorAbort< Kokkos::CudaSpace > { + KOKKOS_INLINE_FUNCTION + static void apply( const size_t rank + , const size_t n0 , const size_t n1 + , const size_t n2 , const size_t n3 + , const size_t n4 , const size_t n5 + , const size_t n6 , const size_t n7 + , const size_t i0 , const size_t i1 + , const size_t i2 , const size_t i3 + , const size_t i4 , const size_t i5 + , const size_t i6 , const size_t i7 ) + { + const int r = + ( n0 <= i0 ? 0 : + ( n1 <= i1 ? 1 : + ( n2 <= i2 ? 2 : + ( n3 <= i3 ? 3 : + ( n4 <= i4 ? 4 : + ( n5 <= i5 ? 5 : + ( n6 <= i6 ? 6 : 7 ))))))); + const size_t n = + ( n0 <= i0 ? n0 : + ( n1 <= i1 ? n1 : + ( n2 <= i2 ? n2 : + ( n3 <= i3 ? n3 : + ( n4 <= i4 ? n4 : + ( n5 <= i5 ? n5 : + ( n6 <= i6 ? n6 : n7 ))))))); + const size_t i = + ( n0 <= i0 ? i0 : + ( n1 <= i1 ? i1 : + ( n2 <= i2 ? i2 : + ( n3 <= i3 ? i3 : + ( n4 <= i4 ? i4 : + ( n5 <= i5 ? i5 : + ( n6 <= i6 ? i6 : i7 ))))))); + printf("Cuda view array bounds error index %d : FAILED %lu < %lu\n" , r , i , n ); + Kokkos::Impl::cuda_abort("Cuda view array bounds error"); + } +}; + +} // namespace Impl +} // namespace Experimental +} // namespace Kokkos + //---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Experimental { +namespace Impl { + // Cuda Texture fetches can be performed for 4, 8 and 16 byte objects (int,int2,int4) // Via reinterpret_case this can be used to support all scalar types of those sizes. // Any other scalar type falls back to either normal reads out of global memory, @@ -130,7 +182,6 @@ struct CudaTextureFetch { CudaTextureFetch( const ValueType * const arg_ptr , Kokkos::Experimental::Impl::SharedAllocationRecord< CudaMemorySpace , void > & record ) - // 'attach_texture_object' returns 0 when __CUDA_ARCH__ < 300 : m_obj( record.template attach_texture_object< AliasType >() ) , m_ptr( arg_ptr ) , m_offset( record.attach_texture_object_offset( reinterpret_cast( arg_ptr ) ) ) diff --git a/lib/kokkos/core/src/Cuda/Kokkos_CudaExec.hpp b/lib/kokkos/core/src/Cuda/Kokkos_CudaExec.hpp index c1b2d51c47..ca03990162 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_CudaExec.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_CudaExec.hpp @@ -208,9 +208,9 @@ struct CudaParallelLaunch< DriverType , true > { Kokkos::Impl::throw_runtime_exception( std::string("CudaParallelLaunch FAILED: shared memory request is too large") ); } else if ( shmem ) { - cudaFuncSetCacheConfig( cuda_parallel_launch_constant_memory< DriverType > , cudaFuncCachePreferShared ); + CUDA_SAFE_CALL( cudaFuncSetCacheConfig( cuda_parallel_launch_constant_memory< DriverType > , cudaFuncCachePreferShared ) ); } else { - cudaFuncSetCacheConfig( cuda_parallel_launch_constant_memory< DriverType > , cudaFuncCachePreferL1 ); + CUDA_SAFE_CALL( cudaFuncSetCacheConfig( cuda_parallel_launch_constant_memory< DriverType > , cudaFuncCachePreferL1 ) ); } // Copy functor to constant memory on the device @@ -246,9 +246,9 @@ struct CudaParallelLaunch< DriverType , false > { Kokkos::Impl::throw_runtime_exception( std::string("CudaParallelLaunch FAILED: shared memory request is too large") ); } else if ( shmem ) { - cudaFuncSetCacheConfig( cuda_parallel_launch_local_memory< DriverType > , cudaFuncCachePreferShared ); + CUDA_SAFE_CALL( cudaFuncSetCacheConfig( cuda_parallel_launch_local_memory< DriverType > , cudaFuncCachePreferShared ) ); } else { - cudaFuncSetCacheConfig( cuda_parallel_launch_local_memory< DriverType > , cudaFuncCachePreferL1 ); + CUDA_SAFE_CALL( cudaFuncSetCacheConfig( cuda_parallel_launch_local_memory< DriverType > , cudaFuncCachePreferL1 ) ); } int* lock_array_ptr = lock_array_cuda_space_ptr(); diff --git a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp index 13316cb63e..829ad03a48 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include /* only compile this file if CUDA is enabled for Kokkos */ @@ -106,6 +107,8 @@ void DeepCopyAsyncCuda( void * dst , const void * src , size_t n) { namespace Kokkos { +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + namespace { void texture_object_attach_impl( Impl::AllocationTracker const & tracker @@ -164,6 +167,8 @@ void CudaSpace::texture_object_attach( Impl::AllocationTracker const & tracker texture_object_attach_impl( tracker, type_size, desc ); } +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + void CudaSpace::access_error() { const std::string msg("Kokkos::CudaSpace::access_error attempt to execute Cuda function from non-Cuda space" ); @@ -178,6 +183,8 @@ void CudaSpace::access_error( const void * const ) /*--------------------------------------------------------------------------*/ +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + Impl::AllocationTracker CudaUVMSpace::allocate_and_track( const std::string & label, const size_t size ) { return Impl::AllocationTracker( allocator(), size, label); @@ -191,6 +198,8 @@ void CudaUVMSpace::texture_object_attach( Impl::AllocationTracker const & track texture_object_attach_impl( tracker, type_size, desc ); } +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + bool CudaUVMSpace::available() { #if defined( CUDA_VERSION ) && ( 6000 <= CUDA_VERSION ) && !defined(__APPLE__) @@ -203,11 +212,15 @@ bool CudaUVMSpace::available() /*--------------------------------------------------------------------------*/ +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + Impl::AllocationTracker CudaHostPinnedSpace::allocate_and_track( const std::string & label, const size_t size ) { return Impl::AllocationTracker( allocator(), size, label); } +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + } // namespace Kokkos /*--------------------------------------------------------------------------*/ @@ -301,8 +314,18 @@ attach_texture_object( const unsigned sizeof_alias , void * const alloc_ptr , size_t const alloc_size ) { - // Only valid for 300 <= __CUDA_ARCH__ - // otherwise return zero. + enum { TEXTURE_BOUND_1D = 1u << 27 }; + + if ( ( alloc_ptr == 0 ) || ( sizeof_alias * TEXTURE_BOUND_1D <= alloc_size ) ) { + std::ostringstream msg ; + msg << "Kokkos::CudaSpace ERROR: Cannot attach texture object to" + << " alloc_ptr(" << alloc_ptr << ")" + << " alloc_size(" << alloc_size << ")" + << " max_size(" << ( sizeof_alias * TEXTURE_BOUND_1D ) << ")" ; + std::cerr << msg.str() << std::endl ; + std::cerr.flush(); + Kokkos::Impl::throw_runtime_exception( msg.str() ); + } ::cudaTextureObject_t tex_obj ; @@ -505,6 +528,133 @@ SharedAllocationRecord( const Kokkos::CudaHostPinnedSpace & arg_space ); } +//---------------------------------------------------------------------------- + +void * SharedAllocationRecord< Kokkos::CudaSpace , void >:: +allocate_tracked( const Kokkos::CudaSpace & arg_space + , const std::string & arg_alloc_label + , const size_t arg_alloc_size ) +{ + if ( ! arg_alloc_size ) return (void *) 0 ; + + SharedAllocationRecord * const r = + allocate( arg_space , arg_alloc_label , arg_alloc_size ); + + RecordBase::increment( r ); + + return r->data(); +} + +void SharedAllocationRecord< Kokkos::CudaSpace , void >:: +deallocate_tracked( void * const arg_alloc_ptr ) +{ + if ( arg_alloc_ptr != 0 ) { + SharedAllocationRecord * const r = get_record( arg_alloc_ptr ); + + RecordBase::decrement( r ); + } +} + +void * SharedAllocationRecord< Kokkos::CudaSpace , void >:: +reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ) +{ + SharedAllocationRecord * const r_old = get_record( arg_alloc_ptr ); + SharedAllocationRecord * const r_new = allocate( r_old->m_space , r_old->get_label() , arg_alloc_size ); + + Kokkos::Impl::DeepCopy( r_new->data() , r_old->data() + , std::min( r_old->size() , r_new->size() ) ); + + RecordBase::increment( r_new ); + RecordBase::decrement( r_old ); + + return r_new->data(); +} + +void * SharedAllocationRecord< Kokkos::CudaUVMSpace , void >:: +allocate_tracked( const Kokkos::CudaUVMSpace & arg_space + , const std::string & arg_alloc_label + , const size_t arg_alloc_size ) +{ + if ( ! arg_alloc_size ) return (void *) 0 ; + + SharedAllocationRecord * const r = + allocate( arg_space , arg_alloc_label , arg_alloc_size ); + + RecordBase::increment( r ); + + return r->data(); +} + +void SharedAllocationRecord< Kokkos::CudaUVMSpace , void >:: +deallocate_tracked( void * const arg_alloc_ptr ) +{ + if ( arg_alloc_ptr != 0 ) { + SharedAllocationRecord * const r = get_record( arg_alloc_ptr ); + + RecordBase::decrement( r ); + } +} + +void * SharedAllocationRecord< Kokkos::CudaUVMSpace , void >:: +reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ) +{ + SharedAllocationRecord * const r_old = get_record( arg_alloc_ptr ); + SharedAllocationRecord * const r_new = allocate( r_old->m_space , r_old->get_label() , arg_alloc_size ); + + Kokkos::Impl::DeepCopy( r_new->data() , r_old->data() + , std::min( r_old->size() , r_new->size() ) ); + + RecordBase::increment( r_new ); + RecordBase::decrement( r_old ); + + return r_new->data(); +} + +void * SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void >:: +allocate_tracked( const Kokkos::CudaHostPinnedSpace & arg_space + , const std::string & arg_alloc_label + , const size_t arg_alloc_size ) +{ + if ( ! arg_alloc_size ) return (void *) 0 ; + + SharedAllocationRecord * const r = + allocate( arg_space , arg_alloc_label , arg_alloc_size ); + + RecordBase::increment( r ); + + return r->data(); +} + +void SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void >:: +deallocate_tracked( void * const arg_alloc_ptr ) +{ + if ( arg_alloc_ptr != 0 ) { + SharedAllocationRecord * const r = get_record( arg_alloc_ptr ); + + RecordBase::decrement( r ); + } +} + +void * SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void >:: +reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ) +{ + SharedAllocationRecord * const r_old = get_record( arg_alloc_ptr ); + SharedAllocationRecord * const r_new = allocate( r_old->m_space , r_old->get_label() , arg_alloc_size ); + + Kokkos::Impl::DeepCopy( r_new->data() , r_old->data() + , std::min( r_old->size() , r_new->size() ) ); + + RecordBase::increment( r_new ); + RecordBase::decrement( r_old ); + + return r_new->data(); +} + +//---------------------------------------------------------------------------- + SharedAllocationRecord< Kokkos::CudaSpace , void > * SharedAllocationRecord< Kokkos::CudaSpace , void >::get_record( void * alloc_ptr ) { @@ -514,15 +664,17 @@ SharedAllocationRecord< Kokkos::CudaSpace , void >::get_record( void * alloc_ptr #if 0 // Copy the header from the allocation - SharedAllocationHeader head ; + Header head ; - SharedAllocationHeader const * const head_cuda = Header::get_header( alloc_ptr ); + Header const * const head_cuda = alloc_ptr ? Header::get_header( alloc_ptr ) : (Header*) 0 ; - Kokkos::Impl::DeepCopy::DeepCopy( & head , head_cuda , sizeof(SharedAllocationHeader) ); + if ( alloc_ptr ) { + Kokkos::Impl::DeepCopy::DeepCopy( & head , head_cuda , sizeof(SharedAllocationHeader) ); + } - RecordCuda * const record = static_cast< RecordCuda * >( head.m_record ); + RecordCuda * const record = alloc_ptr ? static_cast< RecordCuda * >( head.m_record ) : (RecordCuda *) 0 ; - if ( record->m_alloc_ptr != head_cuda ) { + if ( ! alloc_ptr || record->m_alloc_ptr != head_cuda ) { Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::CudaSpace , void >::get_record ERROR" ) ); } @@ -548,9 +700,9 @@ SharedAllocationRecord< Kokkos::CudaUVMSpace , void >::get_record( void * alloc_ using Header = SharedAllocationHeader ; using RecordCuda = SharedAllocationRecord< Kokkos::CudaUVMSpace , void > ; - Header * const h = reinterpret_cast< Header * >( alloc_ptr ) - 1 ; + Header * const h = alloc_ptr ? reinterpret_cast< Header * >( alloc_ptr ) - 1 : (Header *) 0 ; - if ( h->m_record->m_alloc_ptr != h ) { + if ( ! alloc_ptr || h->m_record->m_alloc_ptr != h ) { Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::CudaUVMSpace , void >::get_record ERROR" ) ); } @@ -563,9 +715,9 @@ SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void >::get_record( void * using Header = SharedAllocationHeader ; using RecordCuda = SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void > ; - Header * const h = reinterpret_cast< Header * >( alloc_ptr ) - 1 ; + Header * const h = alloc_ptr ? reinterpret_cast< Header * >( alloc_ptr ) - 1 : (Header *) 0 ; - if ( h->m_record->m_alloc_ptr != h ) { + if ( ! alloc_ptr || h->m_record->m_alloc_ptr != h ) { Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void >::get_record ERROR" ) ); } @@ -592,14 +744,25 @@ print_records( std::ostream & s , const Kokkos::CudaSpace & space , bool detail head.m_label[0] = 0 ; } - snprintf( buffer , 256 , "Cuda addr( 0x%.12lx ) list( 0x%.12lx 0x%.12lx ) extent[ 0x%.12lx + %.8ld ] count(%d) dealloc(0x%.12lx) %s\n" - , reinterpret_cast( r ) - , reinterpret_cast( r->m_prev ) - , reinterpret_cast( r->m_next ) - , reinterpret_cast( r->m_alloc_ptr ) + //Formatting dependent on sizeof(uintptr_t) + const char * format_string; + + if (sizeof(uintptr_t) == sizeof(unsigned long)) { + format_string = "Cuda addr( 0x%.12lx ) list( 0x%.12lx 0x%.12lx ) extent[ 0x%.12lx + %.8ld ] count(%d) dealloc(0x%.12lx) %s\n"; + } + else if (sizeof(uintptr_t) == sizeof(unsigned long long)) { + format_string = "Cuda addr( 0x%.12llx ) list( 0x%.12llx 0x%.12llx ) extent[ 0x%.12llx + %.8ld ] count(%d) dealloc(0x%.12llx) %s\n"; + } + + snprintf( buffer , 256 + , format_string + , reinterpret_cast( r ) + , reinterpret_cast( r->m_prev ) + , reinterpret_cast( r->m_next ) + , reinterpret_cast( r->m_alloc_ptr ) , r->m_alloc_size , r->m_count - , reinterpret_cast( r->m_dealloc ) + , reinterpret_cast( r->m_dealloc ) , head.m_label ); std::cout << buffer ; @@ -612,8 +775,19 @@ print_records( std::ostream & s , const Kokkos::CudaSpace & space , bool detail Kokkos::Impl::DeepCopy::DeepCopy( & head , r->m_alloc_ptr , sizeof(SharedAllocationHeader) ); - snprintf( buffer , 256 , "Cuda [ 0x%.12lx + %ld ] %s\n" - , reinterpret_cast< unsigned long >( r->data() ) + //Formatting dependent on sizeof(uintptr_t) + const char * format_string; + + if (sizeof(uintptr_t) == sizeof(unsigned long)) { + format_string = "Cuda [ 0x%.12lx + %ld ] %s\n"; + } + else if (sizeof(uintptr_t) == sizeof(unsigned long long)) { + format_string = "Cuda [ 0x%.12llx + %ld ] %s\n"; + } + + snprintf( buffer , 256 + , format_string + , reinterpret_cast< uintptr_t >( r->data() ) , r->size() , head.m_label ); diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Alloc.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Alloc.hpp index e1314c0e51..5746176274 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Alloc.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Alloc.hpp @@ -71,7 +71,7 @@ shared_allocation_record( Kokkos::CudaSpace const & arg_space DestructFunctor * const functor = reinterpret_cast< DestructFunctor * >( - reinterpret_cast< unsigned long >( record ) + sizeof(SharedAllocationRecord) ); + reinterpret_cast< uintptr_t >( record ) + sizeof(SharedAllocationRecord) ); new( functor ) DestructFunctor( arg_destruct ); diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.cpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.cpp index 8c8c5e47a5..1f409dffaa 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.cpp @@ -43,6 +43,8 @@ #include +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + /* only compile this file if CUDA is enabled for Kokkos */ #ifdef KOKKOS_HAVE_CUDA @@ -56,6 +58,7 @@ namespace Kokkos { namespace Impl { /*--------------------------------------------------------------------------*/ + TextureAttribute::TextureAttribute( void * const alloc_ptr , size_t alloc_size , cudaChannelFormatDesc const & desc @@ -190,3 +193,6 @@ void * CudaHostAllocator::reallocate(void * old_ptr, size_t old_size, size_t new }} // namespace Kokkos::Impl #endif //KOKKOS_HAVE_CUDA + +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.hpp index 86fe1c901b..58445ab07b 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_BasicAllocators.hpp @@ -46,6 +46,8 @@ #include +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + /* only compile this file if CUDA is enabled for Kokkos */ #ifdef KOKKOS_HAVE_CUDA @@ -85,7 +87,6 @@ struct TextureAttribute : public AllocatorAttributeBase ~TextureAttribute(); }; - /// class CudaUnmanagedAllocator /// does nothing when deallocate(ptr,size) is called struct CudaUnmanagedAllocator @@ -184,4 +185,6 @@ public: #endif //KOKKOS_HAVE_CUDA +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + #endif //KOKKOS_CUDA_BASIC_ALLOCATORS_HPP diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Impl.cpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Impl.cpp index b7c3a62d39..de00b04152 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Impl.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Impl.cpp @@ -222,10 +222,14 @@ private: CudaInternal( const CudaInternal & ); CudaInternal & operator = ( const CudaInternal & ); +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + AllocationTracker m_scratchFlagsTracker; AllocationTracker m_scratchSpaceTracker; AllocationTracker m_scratchUnifiedTracker; +#endif + public: @@ -482,6 +486,32 @@ void CudaInternal::initialize( int cuda_device_id , int stream_count ) Kokkos::Impl::throw_runtime_exception( msg.str() ); } + #ifdef KOKKOS_CUDA_USE_UVM + if(!cuda_launch_blocking()) { + std::cout << "Kokkos::Cuda::initialize WARNING: Cuda is allocating into UVMSpace by default" << std::endl; + std::cout << " without setting CUDA_LAUNCH_BLOCKING=1." << std::endl; + std::cout << " The code must call Cuda::fence() after each kernel" << std::endl; + std::cout << " or will likely crash when accessing data on the host." << std::endl; + } + + const char * env_force_device_alloc = getenv("CUDA_MANAGED_FORCE_DEVICE_ALLOC"); + bool force_device_alloc; + if (env_force_device_alloc == 0) force_device_alloc=false; + else force_device_alloc=atoi(env_force_device_alloc)!=0; + + const char * env_visible_devices = getenv("CUDA_VISIBLE_DEVICES"); + bool visible_devices_one=true; + if (env_visible_devices == 0) visible_devices_one=false; + + if(!visible_devices_one && !force_device_alloc) { + std::cout << "Kokkos::Cuda::initialize WARNING: Cuda is allocating into UVMSpace by default" << std::endl; + std::cout << " without setting CUDA_MANAGED_FORCE_DEVICE_ALLOC=1 or " << std::endl; + std::cout << " setting CUDA_VISIBLE_DEVICES." << std::endl; + std::cout << " This could on multi GPU systems lead to severe performance" << std::endl; + std::cout << " penalties." << std::endl; + } + #endif + // Init the array for used for arbitrarily sized atomics Impl::init_lock_array_cuda_space(); @@ -501,9 +531,27 @@ CudaInternal::scratch_flags( const Cuda::size_type size ) m_scratchFlagsCount = ( size + sizeScratchGrain - 1 ) / sizeScratchGrain ; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + m_scratchFlagsTracker = CudaSpace::allocate_and_track( std::string("InternalScratchFlags") , sizeof( ScratchGrain ) * m_scratchFlagsCount ); + m_scratchFlags = reinterpret_cast(m_scratchFlagsTracker.alloc_ptr()); +#else + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::CudaSpace , void > Record ; + + Record * const r = Record::allocate( Kokkos::CudaSpace() + , "InternalScratchFlags" + , ( sizeof( ScratchGrain ) * m_scratchFlagsCount ) ); + + Record::increment( r ); + + m_scratchFlags = reinterpret_cast( r->data() ); + +#endif + + CUDA_SAFE_CALL( cudaMemset( m_scratchFlags , 0 , m_scratchFlagsCount * sizeScratchGrain ) ); } @@ -517,9 +565,26 @@ CudaInternal::scratch_space( const Cuda::size_type size ) m_scratchSpaceCount = ( size + sizeScratchGrain - 1 ) / sizeScratchGrain ; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + m_scratchSpaceTracker = CudaSpace::allocate_and_track( std::string("InternalScratchSpace") , sizeof( ScratchGrain ) * m_scratchSpaceCount ); + m_scratchSpace = reinterpret_cast(m_scratchSpaceTracker.alloc_ptr()); +#else + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::CudaSpace , void > Record ; + + Record * const r = Record::allocate( Kokkos::CudaSpace() + , "InternalScratchSpace" + , ( sizeof( ScratchGrain ) * m_scratchSpaceCount ) ); + + Record::increment( r ); + + m_scratchSpace = reinterpret_cast( r->data() ); + +#endif + } return m_scratchSpace ; @@ -533,8 +598,26 @@ CudaInternal::scratch_unified( const Cuda::size_type size ) m_scratchUnifiedCount = ( size + sizeScratchGrain - 1 ) / sizeScratchGrain ; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + m_scratchUnifiedTracker = CudaHostPinnedSpace::allocate_and_track( std::string("InternalScratchUnified") , sizeof( ScratchGrain ) * m_scratchUnifiedCount ); + m_scratchUnified = reinterpret_cast( m_scratchUnifiedTracker.alloc_ptr() ); + +#else + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::CudaHostPinnedSpace , void > Record ; + + Record * const r = Record::allocate( Kokkos::CudaHostPinnedSpace() + , "InternalScratchUnified" + , ( sizeof( ScratchGrain ) * m_scratchUnifiedCount ) ); + + Record::increment( r ); + + m_scratchUnified = reinterpret_cast( r->data() ); + +#endif + } return m_scratchUnified ; @@ -555,10 +638,23 @@ void CudaInternal::finalize() ::free( m_stream ); } +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + m_scratchSpaceTracker.clear(); m_scratchFlagsTracker.clear(); m_scratchUnifiedTracker.clear(); +#else + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< CudaSpace > RecordCuda ; + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< CudaHostPinnedSpace > RecordHost ; + + RecordCuda::decrement( RecordCuda::get_record( m_scratchFlags ) ); + RecordCuda::decrement( RecordCuda::get_record( m_scratchSpace ) ); + RecordHost::decrement( RecordHost::get_record( m_scratchUnified ) ); + +#endif + m_cudaDev = -1 ; m_maxWarpCount = 0 ; m_maxBlock = 0 ; diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Internal.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Internal.hpp index dd8a08729b..328857d997 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Internal.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Internal.hpp @@ -43,7 +43,7 @@ #ifndef KOKKOS_CUDA_INTERNAL_HPP #define KOKKOS_CUDA_INTERNAL_HPP - +#include #include /* only compile this file if CUDA is enabled for Kokkos */ @@ -53,18 +53,21 @@ namespace Kokkos { namespace Impl { +template +struct CudaGetMaxBlockSize; + +template +int cuda_get_max_block_size(const typename DriverType::functor_type & f, const size_t vector_length, const size_t shmem_extra) { + return CudaGetMaxBlockSize::get_block_size(f,vector_length, shmem_extra); +} + template -int cuda_get_max_block_size(const typename DriverType::functor_type & f) { -#if ( CUDA_VERSION < 6050 ) - return 256; -#else - bool Large = ( CudaTraits::ConstantMemoryUseThreshold < sizeof(DriverType) ); - - int numBlocks; - if(Large) { +struct CudaGetMaxBlockSize { + static int get_block_size(const typename DriverType::functor_type & f, const size_t vector_length, const size_t shmem_extra) { + int numBlocks; int blockSize=32; - int sharedmem = FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize ); + int sharedmem = shmem_extra + FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize/vector_length ); cudaOccupancyMaxActiveBlocksPerMultiprocessor( &numBlocks, cuda_parallel_launch_constant_memory, @@ -73,7 +76,7 @@ int cuda_get_max_block_size(const typename DriverType::functor_type & f) { while (blockSize<1024 && numBlocks>0) { blockSize*=2; - sharedmem = FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize ); + sharedmem = shmem_extra + FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize/vector_length); cudaOccupancyMaxActiveBlocksPerMultiprocessor( &numBlocks, @@ -83,9 +86,16 @@ int cuda_get_max_block_size(const typename DriverType::functor_type & f) { } if(numBlocks>0) return blockSize; else return blockSize/2; - } else { + } +}; + +template +struct CudaGetMaxBlockSize { + static int get_block_size(const typename DriverType::functor_type & f, const size_t vector_length, const size_t shmem_extra) { + int numBlocks; + int blockSize=32; - int sharedmem = FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize ); + int sharedmem = shmem_extra + FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize/vector_length ); cudaOccupancyMaxActiveBlocksPerMultiprocessor( &numBlocks, cuda_parallel_launch_local_memory, @@ -94,7 +104,7 @@ int cuda_get_max_block_size(const typename DriverType::functor_type & f) { while (blockSize<1024 && numBlocks>0) { blockSize*=2; - sharedmem = FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize ); + sharedmem = shmem_extra + FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize/vector_length ); cudaOccupancyMaxActiveBlocksPerMultiprocessor( &numBlocks, @@ -105,42 +115,58 @@ int cuda_get_max_block_size(const typename DriverType::functor_type & f) { if(numBlocks>0) return blockSize; else return blockSize/2; } -#endif +}; + + + +template +struct CudaGetOptBlockSize; + +template +int cuda_get_opt_block_size(const typename DriverType::functor_type & f, const size_t vector_length, const size_t shmem_extra) { + return CudaGetOptBlockSize::get_block_size(f,vector_length,shmem_extra); } template -int cuda_get_opt_block_size(const typename DriverType::functor_type & f) { -#if ( CUDA_VERSION < 6050 ) - return 256; -#else - bool Large = ( CudaTraits::ConstantMemoryUseThreshold < sizeof(DriverType) ); +struct CudaGetOptBlockSize { + static int get_block_size(const typename DriverType::functor_type & f, const size_t vector_length, const size_t shmem_extra) { + int blockSize=16; + int numBlocks; + int sharedmem; + int maxOccupancy=0; + int bestBlockSize=0; - int blockSize=16; - int numBlocks; - int sharedmem; - int maxOccupancy=0; - int bestBlockSize=0; - - if(Large) { while(blockSize<1024) { blockSize*=2; //calculate the occupancy with that optBlockSize and check whether its larger than the largest one found so far - sharedmem = FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize ); + sharedmem = shmem_extra + FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize/vector_length ); cudaOccupancyMaxActiveBlocksPerMultiprocessor( &numBlocks, cuda_parallel_launch_constant_memory, blockSize, sharedmem); if(maxOccupancy < numBlocks*blockSize) { - maxOccupancy = numBlocks*blockSize; - bestBlockSize = blockSize; + maxOccupancy = numBlocks*blockSize; + bestBlockSize = blockSize; } } - } else { + return bestBlockSize; + } +}; + +template +struct CudaGetOptBlockSize { + static int get_block_size(const typename DriverType::functor_type & f, const size_t vector_length, const size_t shmem_extra) { + int blockSize=16; + int numBlocks; + int sharedmem; + int maxOccupancy=0; + int bestBlockSize=0; + while(blockSize<1024) { blockSize*=2; - sharedmem = FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize ); + sharedmem = shmem_extra + FunctorTeamShmemSize< typename DriverType::functor_type >::value( f , blockSize/vector_length ); cudaOccupancyMaxActiveBlocksPerMultiprocessor( &numBlocks, @@ -153,10 +179,9 @@ int cuda_get_opt_block_size(const typename DriverType::functor_type & f) { bestBlockSize = blockSize; } } + return bestBlockSize; } - return bestBlockSize; -#endif -} +}; }} // namespace Kokkos::Impl diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp index 3aea9be1d9..003aac277c 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp @@ -45,6 +45,7 @@ #define KOKKOS_CUDA_PARALLEL_HPP #include +#include #include #include @@ -252,6 +253,7 @@ private: const int m_league_size ; const int m_team_size ; const int m_vector_length ; + const size_t m_scratch_size ; public: @@ -306,43 +308,135 @@ public: inline int vector_length() const { return m_vector_length ; } inline int team_size() const { return m_team_size ; } inline int league_size() const { return m_league_size ; } + inline size_t scratch_size() const { return m_scratch_size ; } /** \brief Specify league size, request team size */ - TeamPolicy( execution_space & , int league_size_ , int team_size_request , int vector_length_request = 1 ) + TeamPolicy( execution_space & + , int league_size_ + , int team_size_request + , int vector_length_request = 1 ) : m_league_size( league_size_ ) , m_team_size( team_size_request ) - , m_vector_length ( vector_length_request ) + , m_vector_length( vector_length_request ) + , m_scratch_size ( 0 ) { // Allow only power-of-two vector_length - int check = 0; - for(int k = 1; k <= vector_length_max(); k*=2) - if(k == vector_length_request) - check = 1; - if(!check) + if ( ! Kokkos::Impl::is_integral_power_of_two( vector_length_request ) ) { Impl::throw_runtime_exception( "Requested non-power-of-two vector length for TeamPolicy."); + } + + // Make sure league size is permissable + if(league_size_ >= int(Impl::cuda_internal_maximum_grid_count())) + Impl::throw_runtime_exception( "Requested too large league_size for TeamPolicy on Cuda execution space."); + + // Make sure total block size is permissable + if ( m_team_size * m_vector_length > 1024 ) { + Impl::throw_runtime_exception(std::string("Kokkos::TeamPolicy< Cuda > the team size is too large. Team size x vector length must be smaller than 1024.")); + } + } + + /** \brief Specify league size, request team size */ + TeamPolicy( execution_space & + , int league_size_ + , const Kokkos::AUTO_t & /* team_size_request */ + , int vector_length_request = 1 ) + : m_league_size( league_size_ ) + , m_team_size( -1 ) + , m_vector_length( vector_length_request ) + , m_scratch_size ( 0 ) + { + // Allow only power-of-two vector_length + if ( ! Kokkos::Impl::is_integral_power_of_two( vector_length_request ) ) { + Impl::throw_runtime_exception( "Requested non-power-of-two vector length for TeamPolicy."); + } // Make sure league size is permissable if(league_size_ >= int(Impl::cuda_internal_maximum_grid_count())) Impl::throw_runtime_exception( "Requested too large league_size for TeamPolicy on Cuda execution space."); } - TeamPolicy( int league_size_ , int team_size_request , int vector_length_request = 1 ) + TeamPolicy( int league_size_ + , int team_size_request + , int vector_length_request = 1 ) : m_league_size( league_size_ ) , m_team_size( team_size_request ) , m_vector_length ( vector_length_request ) + , m_scratch_size ( 0 ) { // Allow only power-of-two vector_length - int check = 0; - for(int k = 1; k <= vector_length_max(); k*=2) - if(k == vector_length_request) - check = 1; - if(!check) + if ( ! Kokkos::Impl::is_integral_power_of_two( vector_length_request ) ) { Impl::throw_runtime_exception( "Requested non-power-of-two vector length for TeamPolicy."); + } // Make sure league size is permissable if(league_size_ >= int(Impl::cuda_internal_maximum_grid_count())) Impl::throw_runtime_exception( "Requested too large league_size for TeamPolicy on Cuda execution space."); + // Make sure total block size is permissable + if ( m_team_size * m_vector_length > 1024 ) { + Impl::throw_runtime_exception(std::string("Kokkos::TeamPolicy< Cuda > the team size is too large. Team size x vector length must be smaller than 1024.")); + } + } + + TeamPolicy( int league_size_ + , const Kokkos::AUTO_t & /* team_size_request */ + , int vector_length_request = 1 ) + : m_league_size( league_size_ ) + , m_team_size( -1 ) + , m_vector_length ( vector_length_request ) + , m_scratch_size ( 0 ) + { + // Allow only power-of-two vector_length + if ( ! Kokkos::Impl::is_integral_power_of_two( vector_length_request ) ) { + Impl::throw_runtime_exception( "Requested non-power-of-two vector length for TeamPolicy."); + } + + // Make sure league size is permissable + if(league_size_ >= int(Impl::cuda_internal_maximum_grid_count())) + Impl::throw_runtime_exception( "Requested too large league_size for TeamPolicy on Cuda execution space."); + } + + template + TeamPolicy( int league_size_ + , int team_size_request + , const Experimental::TeamScratchRequest & scratch_request ) + : m_league_size( league_size_ ) + , m_team_size( team_size_request ) + , m_vector_length( 1 ) + , m_scratch_size(scratch_request.total(team_size_request)) + { + // Allow only power-of-two vector_length + if ( ! Kokkos::Impl::is_integral_power_of_two( m_vector_length ) ) { + Impl::throw_runtime_exception( "Requested non-power-of-two vector length for TeamPolicy."); + } + + // Make sure league size is permissable + if(league_size_ >= int(Impl::cuda_internal_maximum_grid_count())) + Impl::throw_runtime_exception( "Requested too large league_size for TeamPolicy on Cuda execution space."); + + // Make sure total block size is permissable + if ( m_team_size * m_vector_length > 1024 ) { + Impl::throw_runtime_exception(std::string("Kokkos::TeamPolicy< Cuda > the team size is too large. Team size x vector length must be smaller than 1024.")); + } + } + + template + TeamPolicy( int league_size_ + , const Kokkos::AUTO_t & /* team_size_request */ + , const Experimental::TeamScratchRequest & scratch_request ) + : m_league_size( league_size_ ) + , m_team_size( 256 ) + , m_vector_length ( 1 ) + , m_scratch_size(scratch_request.total(2356)) + { + // Allow only power-of-two vector_length + if ( ! Kokkos::Impl::is_integral_power_of_two( m_vector_length ) ) { + Impl::throw_runtime_exception( "Requested non-power-of-two vector length for TeamPolicy."); + } + + // Make sure league size is permissable + if(league_size_ >= int(Impl::cuda_internal_maximum_grid_count())) + Impl::throw_runtime_exception( "Requested too large league_size for TeamPolicy on Cuda execution space."); } typedef Kokkos::Impl::CudaTeamMember member_type ; @@ -357,35 +451,33 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelFor< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > > +class ParallelFor< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > Policy ; + typedef typename Policy::member_type Member ; + typedef typename Policy::work_tag WorkTag ; const FunctorType m_functor ; const Policy m_policy ; - ParallelFor(); - ParallelFor & operator = ( const ParallelFor & ); + ParallelFor() = delete ; + ParallelFor & operator = ( const ParallelFor & ) = delete ; - template< class Tag > - inline static - __device__ - void driver( const FunctorType & functor - , typename Impl::enable_if< Impl::is_same< Tag , void >::value - , typename Policy::member_type const & >::type iwork - ) - { functor( iwork ); } + template< class TagType > + inline __device__ + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const Member i ) const + { m_functor( i ); } - template< class Tag > - inline static - __device__ - void driver( const FunctorType & functor - , typename Impl::enable_if< ! Impl::is_same< Tag , void >::value - , typename Policy::member_type const & >::type iwork - ) - { functor( Tag() , iwork ); } + template< class TagType > + inline __device__ + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const Member i ) const + { m_functor( TagType() , i ); } public: @@ -395,37 +487,44 @@ public: __device__ void operator()(void) const { - const typename Policy::member_type work_stride = blockDim.y * gridDim.x ; - const typename Policy::member_type work_end = m_policy.end(); + const Member work_stride = blockDim.y * gridDim.x ; + const Member work_end = m_policy.end(); - for ( typename Policy::member_type + for ( Member iwork = m_policy.begin() + threadIdx.y + blockDim.y * blockIdx.x ; iwork < work_end ; iwork += work_stride ) { - ParallelFor::template driver< typename Policy::work_tag >( m_functor, iwork ); + this-> template exec_range< WorkTag >( iwork ); } } - ParallelFor( const FunctorType & functor , - const Policy & policy ) - : m_functor( functor ) - , m_policy( policy ) + inline + void execute() const { + const int nwork = m_policy.end() - m_policy.begin(); const dim3 block( 1 , CudaTraits::WarpSize * cuda_internal_maximum_warp_count(), 1); - const dim3 grid( std::min( ( int( policy.end() - policy.begin() ) + block.y - 1 ) / block.y - , cuda_internal_maximum_grid_count() ) - , 1 , 1); + const dim3 grid( std::min( ( nwork + block.y - 1 ) / block.y , cuda_internal_maximum_grid_count() ) , 1 , 1); CudaParallelLaunch< ParallelFor >( *this , grid , block , 0 ); } + + ParallelFor( const FunctorType & arg_functor , + const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + { } }; template< class FunctorType , class Arg0 , class Arg1 > -class ParallelFor< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Cuda > > +class ParallelFor< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Cuda > + > { private: typedef Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Cuda > Policy ; + typedef typename Policy::member_type Member ; + typedef typename Policy::work_tag WorkTag ; public: @@ -442,20 +541,22 @@ private: // const FunctorType m_functor ; - size_type m_shmem_begin ; - size_type m_shmem_size ; - size_type m_league_size ; + const size_type m_league_size ; + const size_type m_team_size ; + const size_type m_vector_size ; + const size_type m_shmem_begin ; + const size_type m_shmem_size ; template< class TagType > __device__ inline - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member ) const + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const Member & member ) const { m_functor( member ); } template< class TagType > __device__ inline - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member ) const + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const Member & member ) const { m_functor( TagType() , member ); } public: @@ -466,7 +567,7 @@ public: // Iterate this block through the league for ( int league_rank = blockIdx.x ; league_rank < m_league_size ; league_rank += gridDim.x ) { - ParallelFor::template driver< typename Policy::work_tag >( + this-> template exec_team< WorkTag >( typename Policy::member_type( kokkos_impl_cuda_shared_memory() , m_shmem_begin , m_shmem_size @@ -475,28 +576,42 @@ public: } } + inline + void execute() const + { + const int shmem_size_total = m_shmem_begin + m_shmem_size ; + const dim3 grid( int(m_league_size) , 1 , 1 ); + const dim3 block( int(m_vector_size) , int(m_team_size) , 1 ); - ParallelFor( const FunctorType & functor - , const Policy & policy - ) - : m_functor( functor ) - , m_shmem_begin( sizeof(double) * ( policy.team_size() + 2 ) ) - , m_shmem_size( FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ) - , m_league_size( policy.league_size() ) - { - // Functor's reduce memory, team scan memory, and team shared memory depend upon team size. + CudaParallelLaunch< ParallelFor >( *this, grid, block, shmem_size_total ); // copy to device and execute - const int shmem_size_total = m_shmem_begin + m_shmem_size ; - - if ( CudaTraits::SharedMemoryCapacity < shmem_size_total ) { - Kokkos::Impl::throw_runtime_exception(std::string("Kokkos::Impl::ParallelFor< Cuda > insufficient shared memory")); } - const dim3 grid( int(policy.league_size()) , 1 , 1 ); - const dim3 block( policy.vector_length() , policy.team_size() , 1 ); + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy + ) + : m_functor( arg_functor ) + , m_league_size( arg_policy.league_size() ) + , m_team_size( 0 <= arg_policy.team_size() ? arg_policy.team_size() : + Kokkos::Impl::cuda_get_opt_block_size< ParallelFor >( arg_functor , arg_policy.vector_length(), arg_policy.scratch_size() ) / arg_policy.vector_length() ) + , m_vector_size( arg_policy.vector_length() ) + , m_shmem_begin( sizeof(double) * ( m_team_size + 2 ) ) + , m_shmem_size( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( m_functor , m_team_size ) ) + { + // Functor's reduce memory, team scan memory, and team shared memory depend upon team size. - CudaParallelLaunch< ParallelFor >( *this, grid, block, shmem_size_total ); // copy to device and execute - } + const int shmem_size_total = m_shmem_begin + m_shmem_size ; + + if ( CudaTraits::SharedMemoryCapacity < shmem_size_total ) { + Kokkos::Impl::throw_runtime_exception(std::string("Kokkos::Impl::ParallelFor< Cuda > insufficient shared memory")); + } + + if ( m_team_size > + Kokkos::Impl::cuda_get_max_block_size< ParallelFor > + ( arg_functor , arg_policy.vector_length(), arg_policy.scratch_size() ) / arg_policy.vector_length()) { + Kokkos::Impl::throw_runtime_exception(std::string("Kokkos::Impl::ParallelFor< Cuda > requested too large team size.")); + } + } }; } // namespace Impl @@ -509,15 +624,20 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelReduce< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > > +class ParallelReduce< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > + > { private: typedef Kokkos::RangePolicy Policy ; - typedef typename Policy::WorkRange work_range ; - typedef typename Policy::work_tag work_tag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , work_tag > ValueInit ; + + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; public: @@ -529,40 +649,27 @@ public: // Algorithmic constraints: blockSize is a power of two AND blockDim.y == blockDim.z == 1 - const FunctorType m_functor ; - const Policy m_policy ; - size_type * m_scratch_space ; - size_type * m_scratch_flags ; - size_type * m_unified_space ; + const FunctorType m_functor ; + const Policy m_policy ; + const pointer_type m_result_ptr ; + size_type * m_scratch_space ; + size_type * m_scratch_flags ; + size_type * m_unified_space ; - // Determine block size constrained by shared memory: - static inline - unsigned local_block_size( const FunctorType & f ) - { - unsigned n = CudaTraits::WarpSize * 8 ; - while ( n && CudaTraits::SharedMemoryCapacity < cuda_single_inter_block_reduce_scan_shmem( f , n ) ) { n >>= 1 ; } - return n ; - } + template< class TagType > + __device__ inline + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const Member & i , reference_type update ) const + { m_functor( i , update ); } - template< class Tag > - inline static - __device__ - void driver( const FunctorType & functor - , typename Impl::enable_if< Impl::is_same< Tag , void >::value - , typename Policy::member_type const & >::type iwork - , reference_type value ) - { functor( iwork , value ); } + template< class TagType > + __device__ inline + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const Member & i , reference_type update ) const + { m_functor( TagType() , i , update ); } - template< class Tag > - inline static - __device__ - void driver( const FunctorType & functor - , typename Impl::enable_if< ! Impl::is_same< Tag , void >::value - , typename Policy::member_type const & >::type iwork - , reference_type value ) - { functor( Tag() , iwork , value ); } +#if ! defined( KOKKOS_EXPERIMENTAL_CUDA_SHFL_REDUCTION ) -#ifndef KOKKOS_EXPERIMENTAL_CUDA_SHFL_REDUCTION __device__ inline void operator()(void) const { @@ -578,16 +685,16 @@ public: // Accumulate the values for this block. // The accumulation ordering does not match the final pass, but is arithmatically equivalent. - const work_range range( m_policy , blockIdx.x , gridDim.x ); + const WorkRange range( m_policy , blockIdx.x , gridDim.x ); - for ( typename work_range::member_type iwork = range.begin() + threadIdx.y , iwork_end = range.end() ; + for ( Member iwork = range.begin() + threadIdx.y , iwork_end = range.end() ; iwork < iwork_end ; iwork += blockDim.y ) { - ParallelReduce::template driver< work_tag >( m_functor , iwork , value ); + this-> template exec_range< WorkTag >( iwork , value ); } } // Reduce with final value at blockDim.y - 1 location. - if ( cuda_single_inter_block_reduce_scan( + if ( cuda_single_inter_block_reduce_scan( m_functor , blockIdx.x , gridDim.x , kokkos_impl_cuda_shared_memory() , m_scratch_space , m_scratch_flags ) ) { @@ -597,7 +704,7 @@ public: size_type * const global = m_unified_space ? m_unified_space : m_scratch_space ; if ( threadIdx.y == 0 ) { - Kokkos::Impl::FunctorFinal< FunctorType , work_tag >::final( m_functor , shared ); + Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( m_functor , shared ); } if ( CudaTraits::WarpSize < word_count.value ) { __syncthreads(); } @@ -605,7 +712,9 @@ public: for ( unsigned i = threadIdx.y ; i < word_count.value ; i += blockDim.y ) { global[i] = shared[i]; } } } -#else + +#else /* defined( KOKKOS_EXPERIMENTAL_CUDA_SHFL_REDUCTION ) */ + __device__ inline void operator()(void) const { @@ -619,9 +728,9 @@ public: const Policy range( m_policy , blockIdx.x , gridDim.x ); - for ( typename Policy::member_type iwork = range.begin() + threadIdx.y , iwork_end = range.end() ; + for ( Member iwork = range.begin() + threadIdx.y , iwork_end = range.end() ; iwork < iwork_end ; iwork += blockDim.y ) { - ParallelReduce::template driver< work_tag >( m_functor , iwork , value ); + this-> template exec_range< WorkTag >( iwork , value ); } pointer_type const result = (pointer_type) (m_unified_space ? m_unified_space : m_scratch_space) ; @@ -631,68 +740,92 @@ public: (value,Impl::JoinAdd(),m_scratch_space,result,m_scratch_flags,max_active_thread)) { const unsigned id = threadIdx.y*blockDim.x + threadIdx.x; if(id==0) { - Kokkos::Impl::FunctorFinal< FunctorType , work_tag >::final( m_functor , (void*) &value ); + Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( m_functor , (void*) &value ); *result = value; } } } + #endif - template< class HostViewType > - ParallelReduce( const FunctorType & functor - , const Policy & policy - , const HostViewType & result - ) - : m_functor( functor ) - , m_policy( policy ) - , m_scratch_space( 0 ) - , m_scratch_flags( 0 ) - , m_unified_space( 0 ) - { - const int block_size = local_block_size( functor ); - const int block_count = std::min( int(block_size) - , ( int(policy.end() - policy.begin()) + block_size - 1 ) / block_size - ); - m_scratch_space = cuda_internal_scratch_space( ValueTraits::value_size( functor ) * block_count ); - m_scratch_flags = cuda_internal_scratch_flags( sizeof(size_type) ); - m_unified_space = cuda_internal_scratch_unified( ValueTraits::value_size( functor ) ); + // Determine block size constrained by shared memory: + static inline + unsigned local_block_size( const FunctorType & f ) + { + unsigned n = CudaTraits::WarpSize * 8 ; + while ( n && CudaTraits::SharedMemoryCapacity < cuda_single_inter_block_reduce_scan_shmem( f , n ) ) { n >>= 1 ; } + return n ; + } + + inline + void execute() + { + const int block_size = local_block_size( m_functor ); + + m_scratch_space = cuda_internal_scratch_space( ValueTraits::value_size( m_functor ) * block_size /* block_size == max block_count */ ); + m_scratch_flags = cuda_internal_scratch_flags( sizeof(size_type) ); + m_unified_space = cuda_internal_scratch_unified( ValueTraits::value_size( m_functor ) ); + + const int nwork = m_policy.end() - m_policy.begin(); + // REQUIRED ( 1 , N , 1 ) + const dim3 block( 1 , block_size , 1 ); + // Required grid.x <= block.y + const dim3 grid( std::min( int(block.y) , int( ( nwork + block.y - 1 ) / block.y ) ) , 1 , 1 ); - const dim3 grid( block_count , 1 , 1 ); - const dim3 block( 1 , block_size , 1 ); // REQUIRED DIMENSIONS ( 1 , N , 1 ) #ifdef KOKKOS_EXPERIMENTAL_CUDA_SHFL_REDUCTION const int shmem = 0; #else - const int shmem = cuda_single_inter_block_reduce_scan_shmem( m_functor , block.y ); + const int shmem = cuda_single_inter_block_reduce_scan_shmem( m_functor , block.y ); #endif CudaParallelLaunch< ParallelReduce >( *this, grid, block, shmem ); // copy to device and execute Cuda::fence(); - if ( result.ptr_on_device() ) { + if ( m_result_ptr ) { if ( m_unified_space ) { const int count = ValueTraits::value_count( m_functor ); - for ( int i = 0 ; i < count ; ++i ) { result.ptr_on_device()[i] = pointer_type(m_unified_space)[i] ; } + for ( int i = 0 ; i < count ; ++i ) { m_result_ptr[i] = pointer_type(m_unified_space)[i] ; } } else { const int size = ValueTraits::value_size( m_functor ); - DeepCopy( result.ptr_on_device() , m_scratch_space , size ); + DeepCopy( m_result_ptr , m_scratch_space , size ); } } } + + template< class HostViewType > + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const HostViewType & arg_result + ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result.ptr_on_device() ) + , m_scratch_space( 0 ) + , m_scratch_flags( 0 ) + , m_unified_space( 0 ) + { } }; +//---------------------------------------------------------------------------- + template< class FunctorType , class Arg0 , class Arg1 > -class ParallelReduce< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Cuda > > +class ParallelReduce< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Cuda > + > { private: - typedef Kokkos::TeamPolicy Policy ; - typedef typename Policy::work_tag work_tag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , work_tag > ValueInit ; - typedef typename ValueTraits::pointer_type pointer_type ; - typedef typename ValueTraits::reference_type reference_type ; + typedef Kokkos::TeamPolicy Policy ; + typedef typename Policy::member_type Member ; + typedef typename Policy::work_tag WorkTag ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; + + typedef typename ValueTraits::pointer_type pointer_type ; + typedef typename ValueTraits::reference_type reference_type ; public: @@ -709,27 +842,27 @@ private: // [ team shared space ] // - const FunctorType m_functor ; - size_type * m_scratch_space ; - size_type * m_scratch_flags ; - size_type * m_unified_space ; - size_type m_team_begin ; - size_type m_shmem_begin ; - size_type m_shmem_size ; - size_type m_league_size ; + const FunctorType m_functor ; + const pointer_type m_result_ptr ; + size_type * m_scratch_space ; + size_type * m_scratch_flags ; + size_type * m_unified_space ; + size_type m_team_begin ; + size_type m_shmem_begin ; + size_type m_shmem_size ; + const size_type m_league_size ; + const size_type m_team_size ; template< class TagType > __device__ inline - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member - , reference_type update ) const + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const Member & member , reference_type update ) const { m_functor( member , update ); } template< class TagType > __device__ inline - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member - , reference_type update ) const + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const Member & member , reference_type update ) const { m_functor( TagType() , member , update ); } public: @@ -745,9 +878,8 @@ public: // Iterate this block through the league for ( int league_rank = blockIdx.x ; league_rank < m_league_size ; league_rank += gridDim.x ) { - - ParallelReduce::template driver< work_tag > - ( typename Policy::member_type( kokkos_impl_cuda_shared_memory() + m_team_begin + this-> template exec_team< WorkTag > + ( Member( kokkos_impl_cuda_shared_memory() + m_team_begin , m_shmem_begin , m_shmem_size , league_rank @@ -756,7 +888,7 @@ public: } // Reduce with final value at blockDim.y - 1 location. - if ( cuda_single_inter_block_reduce_scan( + if ( cuda_single_inter_block_reduce_scan( m_functor , blockIdx.x , gridDim.x , kokkos_impl_cuda_shared_memory() , m_scratch_space , m_scratch_flags ) ) { @@ -766,7 +898,7 @@ public: size_type * const global = m_unified_space ? m_unified_space : m_scratch_space ; if ( threadIdx.y == 0 ) { - Kokkos::Impl::FunctorFinal< FunctorType , work_tag >::final( m_functor , shared ); + Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( m_functor , shared ); } if ( CudaTraits::WarpSize < word_count.value ) { __syncthreads(); } @@ -775,58 +907,85 @@ public: } } + inline + void execute() + { + const int block_count = std::min( m_league_size , m_team_size ); + + m_scratch_space = cuda_internal_scratch_space( ValueTraits::value_size( m_functor ) * block_count ); + m_scratch_flags = cuda_internal_scratch_flags( sizeof(size_type) ); + m_unified_space = cuda_internal_scratch_unified( ValueTraits::value_size( m_functor ) ); + + // REQUIRED DIMENSIONS ( 1 , N , 1 ) + const dim3 block( 1 , m_team_size , 1 ); + const dim3 grid( std::min( int(m_league_size) , int(m_team_size) ) , 1 , 1 ); + const int shmem_size_total = m_team_begin + m_shmem_begin + m_shmem_size ; + + CudaParallelLaunch< ParallelReduce >( *this, grid, block, shmem_size_total ); // copy to device and execute + + Cuda::fence(); + + if ( m_result_ptr ) { + if ( m_unified_space ) { + const int count = ValueTraits::value_count( m_functor ); + for ( int i = 0 ; i < count ; ++i ) { m_result_ptr[i] = pointer_type(m_unified_space)[i] ; } + } + else { + const int size = ValueTraits::value_size( m_functor ); + DeepCopy( m_result_ptr, m_scratch_space, size ); + } + } + } template< class HostViewType > - ParallelReduce( const FunctorType & functor - , const Policy & policy - , const HostViewType & result + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const HostViewType & arg_result ) - : m_functor( functor ) + : m_functor( arg_functor ) + , m_result_ptr( arg_result.ptr_on_device() ) , m_scratch_space( 0 ) , m_scratch_flags( 0 ) , m_unified_space( 0 ) - , m_team_begin( cuda_single_inter_block_reduce_scan_shmem( functor , policy.team_size() ) ) - , m_shmem_begin( sizeof(double) * ( policy.team_size() + 2 ) ) - , m_shmem_size( FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ) - , m_league_size( policy.league_size() ) + , m_team_begin( 0 ) + , m_shmem_begin( 0 ) + , m_shmem_size( 0 ) + , m_league_size( arg_policy.league_size() ) + , m_team_size( 0 <= arg_policy.team_size() ? arg_policy.team_size() : + Kokkos::Impl::cuda_get_opt_block_size< ParallelReduce >( arg_functor , arg_policy.vector_length(), arg_policy.scratch_size() ) / arg_policy.vector_length() ) { + // Return Init value if the number of worksets is zero + if( arg_policy.league_size() == 0) { + ValueInit::init( m_functor , arg_result.ptr_on_device() ); + return ; + } + + m_team_begin = cuda_single_inter_block_reduce_scan_shmem( arg_functor , m_team_size ); + m_shmem_begin = sizeof(double) * ( m_team_size + 2 ); + m_shmem_size = arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( arg_functor , m_team_size ); // The global parallel_reduce does not support vector_length other than 1 at the moment - if(policy.vector_length() > 1) + if( arg_policy.vector_length() > 1) Impl::throw_runtime_exception( "Kokkos::parallel_reduce with a TeamPolicy using a vector length of greater than 1 is not currently supported for CUDA."); + if( m_team_size < 32) + Impl::throw_runtime_exception( "Kokkos::parallel_reduce with a TeamPolicy using a team_size smaller than 32 is not currently supported with CUDA."); + // Functor's reduce memory, team scan memory, and team shared memory depend upon team size. const int shmem_size_total = m_team_begin + m_shmem_begin + m_shmem_size ; - const int not_power_of_two = 0 != ( policy.team_size() & ( policy.team_size() - 1 ) ); - if ( not_power_of_two || CudaTraits::SharedMemoryCapacity < shmem_size_total ) { + if ( ! Kokkos::Impl::is_integral_power_of_two( m_team_size ) || + CudaTraits::SharedMemoryCapacity < shmem_size_total ) { Kokkos::Impl::throw_runtime_exception(std::string("Kokkos::Impl::ParallelReduce< Cuda > bad team size")); } - const int block_count = std::min( policy.league_size() , policy.team_size() ); - - m_scratch_space = cuda_internal_scratch_space( ValueTraits::value_size( functor ) * block_count ); - m_scratch_flags = cuda_internal_scratch_flags( sizeof(size_type) ); - m_unified_space = cuda_internal_scratch_unified( ValueTraits::value_size( functor ) ); - - const dim3 grid( block_count , 1 , 1 ); - const dim3 block( 1 , policy.team_size() , 1 ); // REQUIRED DIMENSIONS ( 1 , N , 1 ) - - CudaParallelLaunch< ParallelReduce >( *this, grid, block, shmem_size_total ); // copy to device and execute - - Cuda::fence(); - - if ( result.ptr_on_device() ) { - if ( m_unified_space ) { - const int count = ValueTraits::value_count( m_functor ); - for ( int i = 0 ; i < count ; ++i ) { result.ptr_on_device()[i] = pointer_type(m_unified_space)[i] ; } - } - else { - const int size = ValueTraits::value_size( m_functor ); - DeepCopy( result.ptr_on_device() , m_scratch_space , size ); - } + if ( m_team_size > + Kokkos::Impl::cuda_get_max_block_size< ParallelReduce > + ( arg_functor , arg_policy.vector_length(), arg_policy.scratch_size() ) / arg_policy.vector_length()) { + Kokkos::Impl::throw_runtime_exception(std::string("Kokkos::Impl::ParallelReduce< Cuda > requested too large team size.")); } + } }; @@ -840,68 +999,53 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelScan< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > > +class ParallelScan< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Cuda > + > { private: - typedef Kokkos::RangePolicy Policy ; - typedef typename Policy::WorkRange work_range ; - typedef typename Policy::work_tag work_tag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , work_tag > ValueInit ; - typedef Kokkos::Impl::FunctorValueOps< FunctorType , work_tag > ValueOps ; + typedef Kokkos::RangePolicy Policy ; + typedef typename Policy::member_type Member ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::WorkRange WorkRange ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; + typedef Kokkos::Impl::FunctorValueOps< FunctorType, WorkTag > ValueOps ; public: + typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; typedef FunctorType functor_type ; typedef Cuda::size_type size_type ; +private: + // Algorithmic constraints: // (a) blockDim.y is a power of two // (b) blockDim.y == blockDim.z == 1 // (c) gridDim.x <= blockDim.y * blockDim.y // (d) gridDim.y == gridDim.z == 1 - // Determine block size constrained by shared memory: - static inline - unsigned local_block_size( const FunctorType & f ) - { - // blockDim.y must be power of two = 128 (4 warps) or 256 (8 warps) or 512 (16 warps) - // gridDim.x <= blockDim.y * blockDim.y - // - // 4 warps was 10% faster than 8 warps and 20% faster than 16 warps in unit testing - - unsigned n = CudaTraits::WarpSize * 4 ; - while ( n && CudaTraits::SharedMemoryCapacity < cuda_single_inter_block_reduce_scan_shmem( f , n ) ) { n >>= 1 ; } - return n ; - } - const FunctorType m_functor ; const Policy m_policy ; size_type * m_scratch_space ; size_type * m_scratch_flags ; - size_type m_final ; - - template< class Tag > - inline static - __device__ - void driver( const FunctorType & functor - , typename Impl::enable_if< Impl::is_same< Tag , void >::value - , typename Policy::member_type const & >::type iwork - , reference_type value - , const bool final ) - { functor( iwork , value , final ); } + size_type m_final ; - template< class Tag > - inline static - __device__ - void driver( const FunctorType & functor - , typename Impl::enable_if< ! Impl::is_same< Tag , void >::value - , typename Policy::member_type const & >::type iwork - , reference_type value - , const bool final ) - { functor( Tag() , iwork , value , final ); } + template< class TagType > + __device__ inline + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const Member & i , reference_type update , const bool final ) const + { m_functor( i , update , final ); } + + template< class TagType > + __device__ inline + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const Member & i , reference_type update , const bool final ) const + { m_functor( TagType() , i , update , final ); } //---------------------------------------- @@ -920,18 +1064,17 @@ public: // Accumulate the values for this block. // The accumulation ordering does not match the final pass, but is arithmatically equivalent. - const work_range range( m_policy , blockIdx.x , gridDim.x ); + const WorkRange range( m_policy , blockIdx.x , gridDim.x ); - for ( typename Policy::member_type iwork = range.begin() + threadIdx.y , iwork_end = range.end() ; + for ( Member iwork = range.begin() + threadIdx.y , iwork_end = range.end() ; iwork < iwork_end ; iwork += blockDim.y ) { - ParallelScan::template driver< work_tag > - ( m_functor , iwork , ValueOps::reference( shared_value ) , false ); + this-> template exec_range< WorkTag >( iwork , ValueOps::reference( shared_value ) , false ); } // Reduce and scan, writing out scan of blocks' totals and block-groups' totals. // Blocks' scan values are written to 'blockIdx.x' location. // Block-groups' scan values are at: i = ( j * blockDim.y - 1 ) for i < gridDim.x - cuda_single_inter_block_reduce_scan( m_functor , blockIdx.x , gridDim.x , kokkos_impl_cuda_shared_memory() , m_scratch_space , m_scratch_flags ); + cuda_single_inter_block_reduce_scan( m_functor , blockIdx.x , gridDim.x , kokkos_impl_cuda_shared_memory() , m_scratch_space , m_scratch_flags ); } //---------------------------------------- @@ -956,7 +1099,7 @@ public: ValueInit::init( m_functor , shared_accum ); } - const work_range range( m_policy , blockIdx.x , gridDim.x ); + const WorkRange range( m_policy , blockIdx.x , gridDim.x ); for ( typename Policy::member_type iwork_base = range.begin(); iwork_base < range.end() ; iwork_base += blockDim.y ) { @@ -975,12 +1118,11 @@ public: // Call functor to accumulate inclusive scan value for this work item if ( iwork < range.end() ) { - ParallelScan::template driver< work_tag > - ( m_functor , iwork , ValueOps::reference( shared_prefix + word_count.value ) , false ); + this-> template exec_range< WorkTag >( iwork , ValueOps::reference( shared_prefix + word_count.value ) , false ); } // Scan block values into locations shared_data[1..blockDim.y] - cuda_intra_block_reduce_scan( m_functor , ValueTraits::pointer_type(shared_data+word_count.value) ); + cuda_intra_block_reduce_scan( m_functor , ValueTraits::pointer_type(shared_data+word_count.value) ); { size_type * const block_total = shared_data + word_count.value * blockDim.y ; @@ -989,12 +1131,13 @@ public: // Call functor with exclusive scan value if ( iwork < range.end() ) { - ParallelScan::template driver< work_tag > - ( m_functor , iwork , ValueOps::reference( shared_prefix ) , true ); + this-> template exec_range< WorkTag >( iwork , ValueOps::reference( shared_prefix ) , true ); } } } +public: + //---------------------------------------- __device__ inline @@ -1008,44 +1151,63 @@ public: } } - ParallelScan( const FunctorType & functor , - const Policy & policy ) - : m_functor( functor ) - , m_policy( policy ) + // Determine block size constrained by shared memory: + static inline + unsigned local_block_size( const FunctorType & f ) + { + // blockDim.y must be power of two = 128 (4 warps) or 256 (8 warps) or 512 (16 warps) + // gridDim.x <= blockDim.y * blockDim.y + // + // 4 warps was 10% faster than 8 warps and 20% faster than 16 warps in unit testing + + unsigned n = CudaTraits::WarpSize * 4 ; + while ( n && CudaTraits::SharedMemoryCapacity < cuda_single_inter_block_reduce_scan_shmem( f , n ) ) { n >>= 1 ; } + return n ; + } + + inline + void execute() + { + enum { GridMaxComputeCapability_2x = 0x0ffff }; + + const int block_size = local_block_size( m_functor ); + + const int grid_max = + ( block_size * block_size ) < GridMaxComputeCapability_2x ? + ( block_size * block_size ) : GridMaxComputeCapability_2x ; + + // At most 'max_grid' blocks: + const int nwork = m_policy.end() - m_policy.begin(); + const int max_grid = std::min( int(grid_max) , int(( nwork + block_size - 1 ) / block_size )); + + // How much work per block: + const int work_per_block = ( nwork + max_grid - 1 ) / max_grid ; + + // How many block are really needed for this much work: + const int grid_x = ( nwork + work_per_block - 1 ) / work_per_block ; + + m_scratch_space = cuda_internal_scratch_space( ValueTraits::value_size( m_functor ) * grid_x ); + m_scratch_flags = cuda_internal_scratch_flags( sizeof(size_type) * 1 ); + + const dim3 grid( grid_x , 1 , 1 ); + const dim3 block( 1 , block_size , 1 ); // REQUIRED DIMENSIONS ( 1 , N , 1 ) + const int shmem = ValueTraits::value_size( m_functor ) * ( block_size + 2 ); + + m_final = false ; + CudaParallelLaunch< ParallelScan >( *this, grid, block, shmem ); // copy to device and execute + + m_final = true ; + CudaParallelLaunch< ParallelScan >( *this, grid, block, shmem ); // copy to device and execute + } + + ParallelScan( const FunctorType & arg_functor , + const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) , m_scratch_space( 0 ) , m_scratch_flags( 0 ) , m_final( false ) - { - enum { GridMaxComputeCapability_2x = 0x0ffff }; - - const int block_size = local_block_size( functor ); - - const int grid_max = ( block_size * block_size ) < GridMaxComputeCapability_2x ? - ( block_size * block_size ) : GridMaxComputeCapability_2x ; - - // At most 'max_grid' blocks: - const int nwork = policy.end() - policy.begin(); - const int max_grid = std::min( int(grid_max) , int(( nwork + block_size - 1 ) / block_size )); - - // How much work per block: - const int work_per_block = ( nwork + max_grid - 1 ) / max_grid ; - - // How many block are really needed for this much work: - const dim3 grid( ( nwork + work_per_block - 1 ) / work_per_block , 1 , 1 ); - const dim3 block( 1 , block_size , 1 ); // REQUIRED DIMENSIONS ( 1 , N , 1 ) - const int shmem = ValueTraits::value_size( functor ) * ( block_size + 2 ); - - m_scratch_space = cuda_internal_scratch_space( ValueTraits::value_size( functor ) * grid.x ); - m_scratch_flags = cuda_internal_scratch_flags( sizeof(size_type) * 1 ); - - m_final = false ; - CudaParallelLaunch< ParallelScan >( *this, grid, block, shmem ); // copy to device and execute - - m_final = true ; - CudaParallelLaunch< ParallelScan >( *this, grid, block, shmem ); // copy to device and execute - } - - void wait() const { Cuda::fence(); } + { } }; } // namespace Impl @@ -1519,7 +1681,7 @@ void parallel_reduce( const ExecPolicy & policy , const ViewType & result_view , const std::string& str = "" , typename Impl::enable_if< - ( Impl::is_view::value && ! Impl::is_integral< ExecPolicy >::value && + ( Kokkos::is_view::value && ! Impl::is_integral< ExecPolicy >::value && Impl::is_same::value )>::type * = 0 ) { @@ -1533,8 +1695,12 @@ void parallel_reduce( const ExecPolicy & policy Kokkos::Experimental::beginParallelScan("" == str ? typeid(FunctorType).name() : str, 0, &kpID); } #endif - - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( functor , policy , result_view ); + + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( functor , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -1551,7 +1717,7 @@ void parallel_reduce( const ExecPolicy & policy , ResultType& result_ref , const std::string& str = "" , typename Impl::enable_if< - ( ! Impl::is_view::value && + ( ! Kokkos::is_view::value && ! Impl::IsNonTrivialReduceFunctor::value && ! Impl::is_integral< ExecPolicy >::value && Impl::is_same::value )>::type * = 0 ) @@ -1583,7 +1749,11 @@ void parallel_reduce( const ExecPolicy & policy } #endif - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( FunctorType(functor_in) , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( FunctorType(functor_in) , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -1630,7 +1800,11 @@ void parallel_reduce( const ExecPolicy & policy } #endif - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( functor , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( functor , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -1646,7 +1820,7 @@ void parallel_reduce( const size_t work_count , const FunctorTypeIn & functor_in , const ViewType & result_view , const std::string& str = "" - , typename Impl::enable_if<( Impl::is_view::value && + , typename Impl::enable_if<( Kokkos::is_view::value && Impl::is_same< typename Impl::FunctorPolicyExecutionSpace< FunctorTypeIn , void >::execution_space, Kokkos::Cuda>::value @@ -1670,7 +1844,11 @@ void parallel_reduce( const size_t work_count } #endif - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( functor , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( functor , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -1687,7 +1865,7 @@ void parallel_reduce( const size_t work_count , const FunctorTypeIn & functor_in , ResultType& result , const std::string& str = "" - , typename Impl::enable_if< ! Impl::is_view::value && + , typename Impl::enable_if< ! Kokkos::is_view::value && ! Impl::IsNonTrivialReduceFunctor::value && Impl::is_same< typename Impl::FunctorPolicyExecutionSpace< FunctorTypeIn , void >::execution_space, @@ -1728,7 +1906,11 @@ void parallel_reduce( const size_t work_count } #endif - (void) Impl::ParallelReduce< FunctorType , ExecPolicy >( FunctorType(functor_in) , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType , ExecPolicy > closure( FunctorType(functor_in) , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -1783,7 +1965,11 @@ void parallel_reduce( const size_t work_count } #endif - (void) Impl::ParallelReduce< FunctorType , ExecPolicy >( functor , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType , ExecPolicy > closure( functor , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp index 5ef16711ee..11871a6abc 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp @@ -117,7 +117,7 @@ inline void cuda_inter_warp_reduction( ValueType& value, value = result[0]; - for(int i = 1; (i*step<=max_active_thread) && i #include +#include #include #include @@ -89,6 +90,8 @@ struct AssertShapeBoundsAbort< CudaSpace > //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + namespace Kokkos { namespace Impl { @@ -419,6 +422,8 @@ public: } } +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- diff --git a/lib/kokkos/core/src/KokkosExp_View.hpp b/lib/kokkos/core/src/KokkosExp_View.hpp index fef76a4570..1fb11abde8 100644 --- a/lib/kokkos/core/src/KokkosExp_View.hpp +++ b/lib/kokkos/core/src/KokkosExp_View.hpp @@ -45,12 +45,14 @@ #define KOKKOS_EXPERIMENTAL_VIEW_HPP #include +#include #include #include #include #include #include +#include //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -59,17 +61,36 @@ namespace Kokkos { namespace Experimental { namespace Impl { +template< class DstMemorySpace , class SrcMemorySpace > +struct DeepCopy ; + template< class DataType > struct ViewArrayAnalysis ; -template< class DataType , class ValueType , class ArrayLayout > +template< class DataType , class ArrayLayout + , typename ValueType = + typename ViewArrayAnalysis< DataType >::non_const_value_type + > struct ViewDataAnalysis ; -template< class , class = void , typename Enable = void > -class ViewMapping { enum { is_assignable = false }; }; +template< class , class ... > +class ViewMapping { public: enum { is_assignable = false }; }; -template< class DstMemorySpace , class SrcMemorySpace > -struct DeepCopy ; +template< class MemorySpace > +struct ViewOperatorBoundsErrorAbort ; + +template<> +struct ViewOperatorBoundsErrorAbort< Kokkos::HostSpace > { + static void apply( const size_t rank + , const size_t n0 , const size_t n1 + , const size_t n2 , const size_t n3 + , const size_t n4 , const size_t n5 + , const size_t n6 , const size_t n7 + , const size_t i0 , const size_t i1 + , const size_t i2 , const size_t i3 + , const size_t i4 , const size_t i5 + , const size_t i6 , const size_t i7 ); +}; } /* namespace Impl */ } /* namespace Experimental */ @@ -87,102 +108,125 @@ namespace Experimental { * This is an implementation detail of View. It is only of interest * to developers implementing a new specialization of View. * - * Template argument permutations: - * - View< DataType , void , void , void > - * - View< DataType , Space , void , void > - * - View< DataType , Space , MemoryTraits , void > - * - View< DataType , Space , void , MemoryTraits > - * - View< DataType , ArrayLayout , void , void > - * - View< DataType , ArrayLayout , Space , void > - * - View< DataType , ArrayLayout , MemoryTraits , void > - * - View< DataType , ArrayLayout , Space , MemoryTraits > - * - View< DataType , MemoryTraits , void , void > + * Template argument options: + * - View< DataType > + * - View< DataType , Space > + * - View< DataType , Space , MemoryTraits > + * - View< DataType , ArrayLayout > + * - View< DataType , ArrayLayout , Space > + * - View< DataType , ArrayLayout , MemoryTraits > + * - View< DataType , ArrayLayout , Space , MemoryTraits > + * - View< DataType , MemoryTraits > */ -template< class DataType , - class Arg1 = void , - class Arg2 = void , - class Arg3 = void > -class ViewTraits { +template< class DataType , class ... Properties > +struct ViewTraits ; + +template<> +struct ViewTraits< void > +{ + typedef void execution_space ; + typedef void memory_space ; + typedef void array_layout ; + typedef void memory_traits ; +}; + +template< class ... Prop > +struct ViewTraits< void , void , Prop ... > +{ + // Ignore an extraneous 'void' + typedef typename ViewTraits::execution_space execution_space ; + typedef typename ViewTraits::memory_space memory_space ; + typedef typename ViewTraits::array_layout array_layout ; + typedef typename ViewTraits::memory_traits memory_traits ; +}; + +template< class ArrayLayout , class ... Prop > +struct ViewTraits< typename std::enable_if< Kokkos::Impl::is_array_layout::value >::type , ArrayLayout , Prop ... > +{ + // Specify layout, keep subsequent space and memory traits arguments + + typedef typename ViewTraits::execution_space execution_space ; + typedef typename ViewTraits::memory_space memory_space ; + typedef ArrayLayout array_layout ; + typedef typename ViewTraits::memory_traits memory_traits ; +}; + +template< class Space , class ... Prop > +struct ViewTraits< typename std::enable_if< Kokkos::Impl::is_space::value >::type , Space , Prop ... > +{ + // Specify Space, memory traits should be the only subsequent argument + + static_assert( std::is_same< typename ViewTraits::execution_space , void >::value || + std::is_same< typename ViewTraits::memory_space , void >::value || + std::is_same< typename ViewTraits::array_layout , void >::value + , "Only one View Execution or Memory Space template argument" ); + + typedef typename Space::execution_space execution_space ; + typedef typename Space::memory_space memory_space ; + typedef typename execution_space::array_layout array_layout ; + typedef typename ViewTraits::memory_traits memory_traits ; +}; + +template< class MemoryTraits , class ... Prop > +struct ViewTraits< typename std::enable_if< Kokkos::Impl::is_memory_traits::value >::type , MemoryTraits , Prop ... > +{ + // Specify memory trait, should not be any subsequent arguments + + static_assert( std::is_same< typename ViewTraits::execution_space , void >::value || + std::is_same< typename ViewTraits::memory_space , void >::value || + std::is_same< typename ViewTraits::array_layout , void >::value || + std::is_same< typename ViewTraits::memory_traits , void >::value + , "MemoryTrait is the final optional template argument for a View" ); + + typedef void execution_space ; + typedef void memory_space ; + typedef void array_layout ; + typedef MemoryTraits memory_traits ; +}; + + +template< class DataType , class ... Properties > +struct ViewTraits { private: - // Layout, Space, and MemoryTraits are optional - // but need to appear in that order. That means Layout - // can only be Arg1, Space can be Arg1 or Arg2, and - // MemoryTraits can be Arg1, Arg2 or Arg3 + // Unpack the properties arguments + typedef ViewTraits< void , Properties ... > prop ; - enum { Arg1IsLayout = Kokkos::Impl::is_array_layout::value }; + typedef typename + std::conditional< ! std::is_same< typename prop::execution_space , void >::value + , typename prop::execution_space + , Kokkos::DefaultExecutionSpace + >::type + ExecutionSpace ; - enum { Arg1IsSpace = Kokkos::Impl::is_space::value }; - enum { Arg2IsSpace = Kokkos::Impl::is_space::value }; - - enum { Arg1IsMemoryTraits = Kokkos::Impl::is_memory_traits::value }; - enum { Arg2IsMemoryTraits = Kokkos::Impl::is_memory_traits::value }; - enum { Arg3IsMemoryTraits = Kokkos::Impl::is_memory_traits::value }; - - enum { Arg1IsVoid = std::is_same< Arg1 , void >::value }; - enum { Arg2IsVoid = std::is_same< Arg2 , void >::value }; - enum { Arg3IsVoid = std::is_same< Arg3 , void >::value }; - - static_assert( 1 == Arg1IsLayout + Arg1IsSpace + Arg1IsMemoryTraits + Arg1IsVoid - , "Template argument #1 must be layout, space, traits, or void" ); - - // If Arg1 is Layout then Arg2 is Space, MemoryTraits, or void - // If Arg1 is Space then Arg2 is MemoryTraits or void - // If Arg1 is MemoryTraits then Arg2 is void - // If Arg1 is Void then Arg2 is void - - static_assert( ( Arg1IsLayout && ( 1 == Arg2IsSpace + Arg2IsMemoryTraits + Arg2IsVoid ) ) || - ( Arg1IsSpace && ( 0 == Arg2IsSpace ) && ( 1 == Arg2IsMemoryTraits + Arg2IsVoid ) ) || - ( Arg1IsMemoryTraits && Arg2IsVoid ) || - ( Arg1IsVoid && Arg2IsVoid ) - , "Template argument #2 must be space, traits, or void" ); - - // Arg3 is MemoryTraits or void and at most one argument is MemoryTraits - static_assert( ( 1 == Arg3IsMemoryTraits + Arg3IsVoid ) && - ( Arg1IsMemoryTraits + Arg2IsMemoryTraits + Arg3IsMemoryTraits <= 1 ) - , "Template argument #3 must be traits or void" ); - - typedef - typename std::conditional< Arg1IsSpace , Arg1 , - typename std::conditional< Arg2IsSpace , Arg2 , Kokkos::DefaultExecutionSpace - >::type >::type::execution_space - ExecutionSpace ; - - typedef - typename std::conditional< Arg1IsSpace , Arg1 , - typename std::conditional< Arg2IsSpace , Arg2 , Kokkos::DefaultExecutionSpace - >::type >::type::memory_space + typedef typename + std::conditional< ! std::is_same< typename prop::memory_space , void >::value + , typename prop::memory_space + , typename ExecutionSpace::memory_space + >::type MemorySpace ; - typedef - typename Kokkos::Impl::is_space< - typename std::conditional< Arg1IsSpace , Arg1 , - typename std::conditional< Arg2IsSpace , Arg2 , Kokkos::DefaultExecutionSpace - >::type >::type >::host_mirror_space - HostMirrorSpace ; - - typedef - typename std::conditional< Arg1IsLayout , Arg1 , typename ExecutionSpace::array_layout >::type + typedef typename + std::conditional< ! std::is_same< typename prop::array_layout , void >::value + , typename prop::array_layout + , typename ExecutionSpace::array_layout + >::type ArrayLayout ; - // Arg1, Arg2, or Arg3 may be memory traits - typedef - typename std::conditional< Arg1IsMemoryTraits , Arg1 , - typename std::conditional< Arg2IsMemoryTraits , Arg2 , - typename std::conditional< Arg3IsMemoryTraits , Arg3 , MemoryManaged - >::type >::type >::type + typedef typename Kokkos::Impl::is_space< ExecutionSpace >::host_mirror_space + HostMirrorSpace ; + + typedef typename + std::conditional< ! std::is_same< typename prop::memory_traits , void >::value + , typename prop::memory_traits + , typename Kokkos::MemoryManaged + >::type MemoryTraits ; - // Analyze data type's array properties - typedef Kokkos::Experimental::Impl::ViewArrayAnalysis< DataType > array_analysis ; - - // Analyze data type's properties with opportunity to specialize based upon the array value type - typedef Kokkos::Experimental::Impl:: - ViewDataAnalysis< DataType - , typename array_analysis::non_const_value_type - , ArrayLayout - > data_analysis ; + // Analyze data type's properties, + // May be specialized based upon the layout and value type + typedef Kokkos::Experimental::Impl::ViewDataAnalysis< DataType , ArrayLayout > data_analysis ; public: @@ -220,17 +264,17 @@ public: //------------------------------------ // Execution space, memory space, memory access traits, and host mirror space. - typedef ExecutionSpace execution_space ; - typedef MemorySpace memory_space ; - typedef Device device_type ; - typedef MemoryTraits memory_traits ; - typedef HostMirrorSpace host_mirror_space ; + typedef ExecutionSpace execution_space ; + typedef MemorySpace memory_space ; + typedef Kokkos::Device device_type ; + typedef MemoryTraits memory_traits ; + typedef HostMirrorSpace host_mirror_space ; - typedef typename memory_space::size_type size_type ; + typedef typename MemorySpace::size_type size_type ; - enum { is_hostspace = std::is_same< memory_space , HostSpace >::value }; - enum { is_managed = memory_traits::Unmanaged == 0 }; - enum { is_random_access = memory_traits::RandomAccess == 1 }; + enum { is_hostspace = std::is_same< MemorySpace , HostSpace >::value }; + enum { is_managed = MemoryTraits::Unmanaged == 0 }; + enum { is_random_access = MemoryTraits::RandomAccess == 1 }; //------------------------------------ }; @@ -254,11 +298,13 @@ public: * they may occur. * * Valid ways in which template arguments may be specified: - * - View< DataType , Space > - * - View< DataType , Space , MemoryTraits > - * - View< DataType , Space , void , MemoryTraits > + * - View< DataType > + * - View< DataType , Layout > * - View< DataType , Layout , Space > * - View< DataType , Layout , Space , MemoryTraits > + * - View< DataType , Space > + * - View< DataType , Space , MemoryTraits > + * - View< DataType , MemoryTraits > * * \tparam DataType (required) This indicates both the type of each * entry of the array, and the combination of compile-time and @@ -315,10 +361,7 @@ public: * } * \endcode */ -template< class DataType - , class Arg1 = void /* ArrayLayout, SpaceType, or MemoryTraits */ - , class Arg2 = void /* SpaceType or MemoryTraits */ - , class Arg3 = void /* MemoryTraits */ > +template< class DataType , class ... Properties > class View ; } /* namespace Experimental */ @@ -376,21 +419,24 @@ view_alloc( Args ... args ) namespace Kokkos { namespace Experimental { -/**\brief Each R? template argument designates whether the subview argument is a range */ -template< class V - , bool R0 = false , bool R1 = false , bool R2 = false , bool R3 = false - , bool R4 = false , bool R5 = false , bool R6 = false , bool R7 = false > -using Subview = typename Kokkos::Experimental::Impl::SubviewType< V, R0 , R1 , R2 , R3 , R4 , R5 , R6 , R7 >::type ; +template< class DataType , class ... Properties > +class View ; -template< class DataType , class Arg1 , class Arg2 , class Arg3 > -class View : public ViewTraits< DataType , Arg1 , Arg2 , Arg3 > { +template< class > struct is_view : public std::false_type {}; + +template< class D, class ... P > +struct is_view< View > : public std::true_type {}; + +template< class DataType , class ... Properties > +class View : public ViewTraits< DataType , Properties ... > { private: - template< class , class , class , class > friend class View ; + template< class , class ... > friend class View ; + template< class , class ... > friend class Impl::ViewMapping ; - typedef ViewTraits< DataType , Arg1 , Arg2 , Arg3 > traits ; - typedef Kokkos::Experimental::Impl::ViewMapping< traits > map_type ; - typedef Kokkos::Experimental::Impl::SharedAllocationTracker track_type ; + typedef ViewTraits< DataType , Properties ... > traits ; + typedef Kokkos::Experimental::Impl::ViewMapping< traits , void > map_type ; + typedef Kokkos::Experimental::Impl::SharedAllocationTracker track_type ; track_type m_track ; map_type m_map ; @@ -414,16 +460,15 @@ public: /** \brief Compatible view of non-const data type */ typedef View< typename traits::non_const_data_type , - typename traits::array_layout , - typename traits::device_type , - typename traits::memory_traits > + typename traits::array_layout , + typename traits::device_type , + typename traits::memory_traits > non_const_type ; /** \brief Compatible HostMirror view */ typedef View< typename traits::non_const_data_type , typename traits::array_layout , - typename traits::host_mirror_space , - void > + typename traits::host_mirror_space > HostMirror ; //---------------------------------------- @@ -431,6 +476,21 @@ public: enum { Rank = map_type::Rank }; + template< typename iType > + KOKKOS_INLINE_FUNCTION constexpr + typename std::enable_if< std::is_integral::value , size_t >::type + extent( const iType & r ) const + { + return r == 0 ? m_map.dimension_0() : ( + r == 1 ? m_map.dimension_1() : ( + r == 2 ? m_map.dimension_2() : ( + r == 3 ? m_map.dimension_3() : ( + r == 4 ? m_map.dimension_4() : ( + r == 5 ? m_map.dimension_5() : ( + r == 6 ? m_map.dimension_6() : ( + r == 7 ? m_map.dimension_7() : 1 ))))))); + } + KOKKOS_INLINE_FUNCTION constexpr size_t dimension_0() const { return m_map.dimension_0(); } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_1() const { return m_map.dimension_1(); } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_2() const { return m_map.dimension_2(); } @@ -440,6 +500,15 @@ public: KOKKOS_INLINE_FUNCTION constexpr size_t dimension_6() const { return m_map.dimension_6(); } KOKKOS_INLINE_FUNCTION constexpr size_t dimension_7() const { return m_map.dimension_7(); } + KOKKOS_INLINE_FUNCTION constexpr size_t size() const { return m_map.dimension_0() * + m_map.dimension_1() * + m_map.dimension_2() * + m_map.dimension_3() * + m_map.dimension_4() * + m_map.dimension_5() * + m_map.dimension_6() * + m_map.dimension_7(); } + KOKKOS_INLINE_FUNCTION constexpr size_t stride_0() const { return m_map.stride_0(); } KOKKOS_INLINE_FUNCTION constexpr size_t stride_1() const { return m_map.stride_1(); } KOKKOS_INLINE_FUNCTION constexpr size_t stride_2() const { return m_map.stride_2(); } @@ -449,21 +518,27 @@ public: KOKKOS_INLINE_FUNCTION constexpr size_t stride_6() const { return m_map.stride_6(); } KOKKOS_INLINE_FUNCTION constexpr size_t stride_7() const { return m_map.stride_7(); } + template< typename iType > + KOKKOS_INLINE_FUNCTION void stride( iType * const s ) const { m_map.stride(s); } + //---------------------------------------- // Range span is the span which contains all members. typedef typename map_type::reference_type reference_type ; + typedef typename map_type::pointer_type pointer_type ; enum { reference_type_is_lvalue_reference = std::is_lvalue_reference< reference_type >::value }; KOKKOS_INLINE_FUNCTION constexpr size_t span() const { return m_map.span(); } + // Deprecated, use 'span()' instead + KOKKOS_INLINE_FUNCTION constexpr size_t capacity() const { return m_map.span(); } KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { return m_map.span_is_contiguous(); } - KOKKOS_INLINE_FUNCTION constexpr typename traits::value_type * data() const { return m_map.data(); } + KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const { return m_map.data(); } // Deprecated, use 'span_is_contigous()' instead KOKKOS_INLINE_FUNCTION constexpr bool is_contiguous() const { return m_map.span_is_contiguous(); } // Deprecated, use 'data()' instead - KOKKOS_INLINE_FUNCTION constexpr typename traits::value_type * ptr_on_device() const { return m_map.data(); } + KOKKOS_INLINE_FUNCTION constexpr pointer_type ptr_on_device() const { return m_map.data(); } //---------------------------------------- // Allow specializations to query their specialized map @@ -485,14 +560,81 @@ private: , Kokkos::Experimental::Impl::Error_view_scalar_reference_to_non_scalar_view >::type scalar_operator_index_type ; + enum { is_default_map = + std::is_same< typename traits::specialize , void >::value && + ( std::is_same< typename traits::array_layout , Kokkos::LayoutLeft >::value || + std::is_same< typename traits::array_layout , Kokkos::LayoutRight >::value || + std::is_same< typename traits::array_layout , Kokkos::LayoutStride >::value + ) }; + + template < bool F , unsigned R + , typename I0 = int + , typename I1 = int + , typename I2 = int + , typename I3 = int + , typename I4 = int + , typename I5 = int + , typename I6 = int + , typename I7 = int > + struct enable { + enum { value = F && ( R == traits::rank ) && + std::is_integral::value && + std::is_integral::value && + std::is_integral::value && + std::is_integral::value && + std::is_integral::value && + std::is_integral::value && + std::is_integral::value && + std::is_integral::value }; + }; + + KOKKOS_INLINE_FUNCTION + void verify_operator_bounds( size_t i0 = 0 , size_t i1 = 0 , size_t i2 = 0 , size_t i3 = 0 + , size_t i4 = 0 , size_t i5 = 0 , size_t i6 = 0 , size_t i7 = 0 ) const + { + if ( ( m_map.dimension_0() <= i0 ) || + ( m_map.dimension_1() <= i1 ) || + ( m_map.dimension_2() <= i2 ) || + ( m_map.dimension_3() <= i3 ) || + ( m_map.dimension_4() <= i4 ) || + ( m_map.dimension_5() <= i5 ) || + ( m_map.dimension_6() <= i6 ) || + ( m_map.dimension_7() <= i7 ) ) { + Kokkos::Experimental::Impl:: + ViewOperatorBoundsErrorAbort< Kokkos::Impl::ActiveExecutionMemorySpace >:: + apply( Rank + , m_map.dimension_0() , m_map.dimension_1() + , m_map.dimension_2() , m_map.dimension_3() + , m_map.dimension_4() , m_map.dimension_5() + , m_map.dimension_6() , m_map.dimension_7() + , i0 , i1 , i2 , i3 , i4 , i5 , i6 , i7 ); + } + } + +#if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) + +#define KOKKOS_VIEW_OPERATOR_VERIFY( I0 , I1 , I2 , I3 , I4 , I5 , I6 , I7 ) \ + Kokkos::Impl::VerifyExecutionCanAccessMemorySpace \ + < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::verify(); \ + verify_operator_bounds(I0,I1,I2,I3,I4,I5,I6,I7); + +#else + +#define KOKKOS_VIEW_OPERATOR_VERIFY( I0 , I1 , I2 , I3 , I4 , I5 , I6 , I7 ) \ + Kokkos::Impl::VerifyExecutionCanAccessMemorySpace \ + < Kokkos::Impl::ActiveExecutionMemorySpace , typename traits::memory_space >::verify(); + +#endif + public: + //------------------------------ // Rank == 0 KOKKOS_FORCEINLINE_FUNCTION scalar_operator_reference_type operator()() const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, 0, 0, 0, 0, 0, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(0,0,0,0,0,0,0,0) return scalar_operator_reference_type( m_map.reference() ); } @@ -502,275 +644,299 @@ public: , const int i1 = 0 , const int i2 = 0 , const int i3 = 0 , const int i4 = 0 , const int i5 = 0 , const int i6 = 0 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); - return m_map.reference(); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) + return scalar_operator_reference_type( m_map.reference() ); } + //------------------------------ // Rank == 1 template< typename I0 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 1 && std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 1 , I0 >::value , reference_type >::type operator[]( const I0 & i0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, 0, 0, 0, 0, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,0,0,0,0,0,0,0) + return m_map.m_handle[ m_map.m_offset(i0) ]; + } + + template< typename I0 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 1 , I0 >::value , reference_type >::type + operator[]( const I0 & i0 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,0,0,0,0,0,0,0) return m_map.reference(i0); } template< typename I0 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 1 && std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 1 , I0 >::value , reference_type >::type operator()( const I0 & i0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, 0, 0, 0, 0, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,0,0,0,0,0,0,0) + return m_map.m_handle[ m_map.m_offset(i0) ]; + } + + template< typename I0 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 1 , I0 >::value , reference_type >::type + operator()( const I0 & i0 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,0,0,0,0,0,0,0) return m_map.reference(i0); } template< typename I0 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 1 , I0 >::value , reference_type >::type operator()( const I0 & i0 - , typename std::enable_if<( Rank == 1 && std::is_integral::value ), const int >::type i1 - , const int i2 = 0 , const int i3 = 0 + , const int i1 , const int i2 = 0 , const int i3 = 0 , const int i4 = 0 , const int i5 = 0 , const int i6 = 0 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0); } + //------------------------------ // Rank == 2 template< typename I0 , typename I1 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 2 && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 2 , I0 , I1 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, 0, 0, 0, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,0,0,0,0,0,0) + return m_map.m_handle[ m_map.m_offset(i0,i1) ]; + } + + template< typename I0 , typename I1 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 2 , I0 , I1 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,0,0,0,0,0,0) return m_map.reference(i0,i1); } template< typename I0 , typename I1 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 2 , I0 , I1 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 - , typename std::enable_if<( Rank == 2 && - std::is_integral::value && - std::is_integral::value - ), const int >::type i2 - , const int i3 = 0 + , const int i2 , const int i3 = 0 , const int i4 = 0 , const int i5 = 0 , const int i6 = 0 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1); } + //------------------------------ // Rank == 3 template< typename I0 , typename I1 , typename I2 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 3 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 3 , I0 , I1 , I2 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, 0, 0, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,0,0,0,0,0) + return m_map.m_handle[ m_map.m_offset(i0,i1,i2) ]; + } + + template< typename I0 , typename I1 , typename I2 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 3 , I0 , I1 , I2 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,0,0,0,0,0) return m_map.reference(i0,i1,i2); } template< typename I0 , typename I1 , typename I2 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 3 , I0 , I1 , I2 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 - , typename std::enable_if<( Rank == 3 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), const int >::type i3 + , const int i3 , const int i4 = 0 , const int i5 = 0 , const int i6 = 0 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1,i2); } + //------------------------------ // Rank == 4 template< typename I0 , typename I1 , typename I2 , typename I3 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 4 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 4 , I0 , I1 , I2 , I3 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, 0, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,0,0,0,0) + return m_map.m_handle[ m_map.m_offset(i0,i1,i2,i3) ]; + } + + template< typename I0 , typename I1 , typename I2 , typename I3 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 4 , I0 , I1 , I2 , I3 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,0,0,0,0) return m_map.reference(i0,i1,i2,i3); } template< typename I0 , typename I1 , typename I2 , typename I3 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 4 , I0 , I1 , I2 , I3 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 - , typename std::enable_if<( Rank == 4 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), const int >::type i4 + , const int i4 , const int i5 = 0 , const int i6 = 0 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1,i2,i3); } + //------------------------------ // Rank == 5 template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 5 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 5 , I0 , I1 , I2 , I3 , I4 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, 0, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,0,0,0) + return m_map.m_handle[ m_map.m_offset(i0,i1,i2,i3,i4) ]; + } + + template< typename I0 , typename I1 , typename I2 , typename I3 + , typename I4 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 5 , I0 , I1 , I2 , I3 , I4 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 + , const I4 & i4 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,0,0,0) return m_map.reference(i0,i1,i2,i3,i4); } template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 5 , I0 , I1 , I2 , I3 , I4 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 - , typename std::enable_if<( Rank == 5 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), const int >::type i5 + , const int i5 , const int i6 = 0 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1,i2,i3,i4); } + //------------------------------ // Rank == 6 template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 , typename I5 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 6 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 6 , I0 , I1 , I2 , I3 , I4 , I5 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 , const I5 & i5 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, 0, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,0,0) + return m_map.m_handle[ m_map.m_offset(i0,i1,i2,i3,i4,i5) ]; + } + + template< typename I0 , typename I1 , typename I2 , typename I3 + , typename I4 , typename I5 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 6 , I0 , I1 , I2 , I3 , I4 , I5 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 + , const I4 & i4 , const I5 & i5 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,0,0) return m_map.reference(i0,i1,i2,i3,i4,i5); } template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 , typename I5 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 6 , I0 , I1 , I2 , I3 , I4 , I5 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 , const I5 & i5 - , typename std::enable_if<( Rank == 6 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), const int >::type i6 + , const int i6 , const int i7 = 0 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1,i2,i3,i4,i5); } + //------------------------------ // Rank == 7 template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 , typename I5 , typename I6 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 7 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 7 , I0 , I1 , I2 , I3 , I4 , I5 , I6 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 , const I5 & i5 , const I6 & i6 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, 0 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,0) + return m_map.m_handle[ m_map.m_offset(i0,i1,i2,i3,i4,i5,i6) ]; + } + + template< typename I0 , typename I1 , typename I2 , typename I3 + , typename I4 , typename I5 , typename I6 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 7 , I0 , I1 , I2 , I3 , I4 , I5 , I6 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 + , const I4 & i4 , const I5 & i5 , const I6 & i6 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,0) return m_map.reference(i0,i1,i2,i3,i4,i5,i6); } template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 , typename I5 , typename I6 > KOKKOS_FORCEINLINE_FUNCTION - reference_type + typename std::enable_if< View::enable< true , 7 , I0 , I1 , I2 , I3 , I4 , I5 , I6 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 , const I5 & i5 , const I6 & i6 - , typename std::enable_if<( Rank == 7 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), const int >::type i7 + , const int i7 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1,i2,i3,i4,i5,i6); } + //------------------------------ // Rank == 8 template< typename I0 , typename I1 , typename I2 , typename I3 , typename I4 , typename I5 , typename I6 , typename I7 > KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if<( Rank == 8 && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value && - std::is_integral::value - ), reference_type >::type + typename std::enable_if< View::enable< is_default_map , 8 , I0 , I1 , I2 , I3 , I4 , I5 , I6 , I7 >::value , reference_type >::type operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 , const I4 & i4 , const I5 & i5 , const I6 & i6 , const I7 & i7 ) const { - KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( typename traits::memory_space, m_map, Rank, i0, i1, i2, i3, i4, i5, i6, i7 ); + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) + return m_map.m_handle[ m_map.m_offset(i0,i1,i2,i3,i4,i5,i6,i7) ]; + } + + template< typename I0 , typename I1 , typename I2 , typename I3 + , typename I4 , typename I5 , typename I6 , typename I7 > + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if< View::enable< ! is_default_map , 8 , I0 , I1 , I2 , I3 , I4 , I5 , I6 , I7 >::value , reference_type >::type + operator()( const I0 & i0 , const I1 & i1 , const I2 & i2 , const I3 & i3 + , const I4 & i4 , const I5 & i5 , const I6 & i6 , const I7 & i7 ) const + { + KOKKOS_VIEW_OPERATOR_VERIFY(i0,i1,i2,i3,i4,i5,i6,i7) return m_map.reference(i0,i1,i2,i3,i4,i5,i6,i7); } +#undef KOKKOS_VIEW_OPERATOR_VERIFY + //---------------------------------------- + // Standard destructor, constructors, and assignment operators KOKKOS_INLINE_FUNCTION ~View() {} @@ -791,53 +957,57 @@ public: View & operator = ( View && rhs ) { m_track = rhs.m_track ; m_map = rhs.m_map ; return *this ; } //---------------------------------------- + // Compatible view copy constructor and assignment + // may assign unmanaged from managed. - template< class RT , class R1 , class R2 , class R3 > + template< class RT , class ... RP > KOKKOS_INLINE_FUNCTION - View( const View & rhs ) - : m_track( rhs.m_track ) + View( const View & rhs ) + : m_track( rhs.m_track , traits::is_managed ) , m_map() { - typedef typename View::traits SrcTraits ; - typedef Kokkos::Experimental::Impl::ViewMapping< traits , SrcTraits > Mapping ; + typedef typename View::traits SrcTraits ; + typedef Kokkos::Experimental::Impl::ViewMapping< traits , SrcTraits , void > Mapping ; static_assert( Mapping::is_assignable , "Incompatible View copy construction" ); Mapping::assign( m_map , rhs.m_map , rhs.m_track ); } - template< class RT , class R1 , class R2 , class R3 > + template< class RT , class ... RP > KOKKOS_INLINE_FUNCTION - View( View && rhs ) - : m_track( rhs.m_track ) - , m_map() + View & operator = ( const View & rhs ) { - typedef typename View::traits SrcTraits ; - typedef Kokkos::Experimental::Impl::ViewMapping< traits , SrcTraits > Mapping ; - static_assert( Mapping::is_assignable , "Incompatible View move construction" ); - Mapping::assign( m_map , rhs.m_map , rhs.m_track ); - } - - template< class RT , class R1 , class R2 , class R3 > - KOKKOS_INLINE_FUNCTION - View & operator = ( const View & rhs ) - { - typedef typename View::traits SrcTraits ; - typedef Kokkos::Experimental::Impl::ViewMapping< traits , SrcTraits > Mapping ; + typedef typename View::traits SrcTraits ; + typedef Kokkos::Experimental::Impl::ViewMapping< traits , SrcTraits , void > Mapping ; static_assert( Mapping::is_assignable , "Incompatible View copy assignment" ); Mapping::assign( m_map , rhs.m_map , rhs.m_track ); - m_track.operator=( rhs.m_track ); + m_track.assign( rhs.m_track , traits::is_managed ); return *this ; } - template< class RT , class R1 , class R2 , class R3 > + //---------------------------------------- + // Compatible subview constructor + // may assign unmanaged from managed. + + template< class RT , class ... RP , class Arg0 , class ... Args > KOKKOS_INLINE_FUNCTION - View & operator = ( View && rhs ) + View( const View< RT , RP... > & src_view + , const Arg0 & arg0 , Args ... args ) + : m_track( src_view.m_track , traits::is_managed ) + , m_map() { - typedef typename View::traits SrcTraits ; - typedef Kokkos::Experimental::Impl::ViewMapping< traits , SrcTraits > Mapping ; - static_assert( Mapping::is_assignable , "Incompatible View move assignment" ); - Mapping::assign( m_map , rhs.m_map , rhs.m_track ); - m_track.operator=( rhs.m_track ); - return *this ; + typedef View< RT , RP... > SrcType ; + + typedef Kokkos::Experimental::Impl::ViewMapping + < void /* deduce destination view type from source view traits */ + , typename SrcType::traits + , Arg0 , Args... > Mapping ; + + typedef typename Mapping::type DstType ; + + static_assert( Kokkos::Experimental::Impl::ViewMapping< View , DstType , void >::is_assignable + , "Subview construction requires compatible view and subview arguments" ); + + Mapping::assign( m_map, src_view.m_map, arg0 , args... ); } //---------------------------------------- @@ -851,19 +1021,23 @@ private: map_type m_map ; ExecSpace m_space ; - KOKKOS_INLINE_FUNCTION void destroy_shared_allocation() { m_map.destroy( m_space ); } }; public: + KOKKOS_INLINE_FUNCTION + int use_count() const { return m_track.use_count(); } + inline const std::string label() const { return m_track.template get_label< typename traits::memory_space >(); } + // Disambiguate from subview constructor. template< class Prop > explicit inline View( const Prop & arg_prop - , const size_t arg_N0 = 0 + , typename std::enable_if< ! is_view::value , + const size_t >::type arg_N0 = 0 , const size_t arg_N1 = 0 , const size_t arg_N2 = 0 , const size_t arg_N3 = 0 @@ -902,21 +1076,23 @@ public: // Construct the mapping object prior to start of tracking // to assign destroy functor and possibly initialize. - m_map = map_type( record->data() + m_map = map_type( reinterpret_cast< pointer_type >( record->data() ) , prop.allow_padding , arg_N0 , arg_N1 , arg_N2 , arg_N3 , arg_N4 , arg_N5 , arg_N6 , arg_N7 ); - // Copy the destroy functor into the allocation record before initiating tracking. - record->m_destroy.m_map = m_map ; - record->m_destroy.m_space = prop.execution ; - + // If constructing the plan for destructing as well + // Copy the destroy functor into the allocation record + // before initiating tracking. if ( prop.initialize.value ) { m_map.construct( prop.execution ); + + record->m_destroy.m_map = m_map ; + record->m_destroy.m_space = prop.execution ; } - // Destroy functor assigned and initialization complete, start tracking - m_track = track_type( record ); + // Setup and initialization complete, start tracking + m_track.assign_allocated_record_to_uninitialized( record ); } template< class Prop > @@ -952,18 +1128,19 @@ public: // Construct the mapping object prior to start of tracking // to assign destroy functor and possibly initialize. - m_map = map_type( record->data() , prop.allow_padding , arg_layout ); + m_map = map_type( reinterpret_cast< pointer_type >( record->data() ) , prop.allow_padding , arg_layout ); // Copy the destroy functor into the allocation record before initiating tracking. - record->m_destroy.m_map = m_map ; - record->m_destroy.m_space = prop.execution ; if ( prop.initialize.value ) { m_map.construct( prop.execution ); + + record->m_destroy.m_map = m_map ; + record->m_destroy.m_space = prop.execution ; } - // Destroy functor assigned and initialization complete, start tracking - m_track = track_type( record ); + // Setup and initialization complete, start tracking + m_track.assign_allocated_record_to_uninitialized( record ); } //---------------------------------------- @@ -984,7 +1161,7 @@ public: } explicit inline - View( typename traits::value_type * const arg_ptr + View( pointer_type arg_ptr , const size_t arg_N0 = 0 , const size_t arg_N1 = 0 , const size_t arg_N2 = 0 @@ -1001,7 +1178,7 @@ public: {} explicit inline - View( typename traits::value_type * const arg_ptr + View( pointer_type arg_ptr , typename traits::array_layout & arg_layout ) : m_track() // No memory tracking @@ -1037,449 +1214,69 @@ public: , const size_t arg_N6 = 0 , const size_t arg_N7 = 0 ) : m_track() // No memory tracking - , m_map( arg_space.get_shmem( map_type::memory_span( std::integral_constant() - , arg_N0 , arg_N1 , arg_N2 , arg_N3 - , arg_N4 , arg_N5 , arg_N6 , arg_N7 ) ) - , std::integral_constant() - , arg_N0 , arg_N1 , arg_N2 , arg_N3 - , arg_N4 , arg_N5 , arg_N6 , arg_N7 ) + , m_map( reinterpret_cast( + arg_space.get_shmem( + map_type::memory_span( std::integral_constant() + , arg_N0 , arg_N1 , arg_N2 , arg_N3 + , arg_N4 , arg_N5 , arg_N6 , arg_N7 ) ) ) + , std::integral_constant() + , arg_N0 , arg_N1 , arg_N2 , arg_N3 + , arg_N4 , arg_N5 , arg_N6 , arg_N7 ) {} - - //---------------------------------------- - // Subviews - -private: - - /**\brief Private method to support extensibility of subview construction */ - KOKKOS_INLINE_FUNCTION - View( const track_type & arg_track , const map_type & arg_map ) - : m_track( arg_track ) - , m_map( arg_map ) - {} - - explicit KOKKOS_INLINE_FUNCTION - View( const track_type & rhs ) - : m_track( rhs ) - , m_map() - {} - -public: - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 , class T6 , class T7 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 , T5 const & arg5 , T6 const & arg6 , T7 const & arg7 - ); - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 , class T6 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 , T5 const & arg5 , T6 const & arg6 - ); - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 , T5 const & arg5 - ); - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 - ); - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 , class T2 , class T3 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - ); - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 , class T2 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 - ); - - template< class D , class A1 , class A2 , class A3 - , class T0 , class T1 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D , A1 , A2 , A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D , A1 , A2 , A3 > & src - , T0 const & arg0 , T1 const & arg1 - ); - - template< class D, class A1, class A2, class A3, class T0 > - friend - KOKKOS_INLINE_FUNCTION - Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > - subview( const View< D, A1, A2, A3 > & src , T0 const & arg0 ); - }; -template< class > struct is_view : public std::false_type {}; - -template< class D, class A1, class A2, class A3 > -struct is_view< View > : public std::true_type {}; - //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -template< class D, class A1, class A2, class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 , class T6 , class T7 > +template< class V , class ... Args > +using Subview = + typename Kokkos::Experimental::Impl::ViewMapping + < void /* deduce subview type from source view traits */ + , V + , Args ... + >::type ; + +template< class D, class ... P , class ... Args > KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 , T5 const & arg5 , T6 const & arg6 , T7 const & arg7 - ) +typename Kokkos::Experimental::Impl::ViewMapping + < void /* deduce subview type from source view traits */ + , ViewTraits< D , P... > + , Args ... + >::type +subview( const View< D, P... > & src , Args ... args ) { - typedef View< D, A1, A2, A3 > SrcView ; + static_assert( View< D , P... >::Rank == sizeof...(Args) , + "subview requires one argument for each source View rank" ); - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 8 , "Subview of rank 8 View requires 8 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 ); - - return dst ; + return typename + Kokkos::Experimental::Impl::ViewMapping + < void /* deduce subview type from source view traits */ + , ViewTraits< D , P ... > + , Args ... >::type( src , args ... ); } -template< class D, class A1, class A2, class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 , class T6 > +template< class MemoryTraits , class D, class ... P , class ... Args > KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 , T5 const & arg5 , T6 const & arg6 - ) +typename Kokkos::Experimental::Impl::ViewMapping + < void /* deduce subview type from source view traits */ + , ViewTraits< D , P... > + , Args ... + >::template apply< MemoryTraits >::type +subview( const View< D, P... > & src , Args ... args ) { - typedef View< D, A1, A2, A3 > SrcView ; + static_assert( View< D , P... >::Rank == sizeof...(Args) , + "subview requires one argument for each source View rank" ); - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 7 , "Subview of rank 7 View requires 7 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, arg2, arg3, arg4, arg5, arg6, 0 ); - - return dst ; + return typename + Kokkos::Experimental::Impl::ViewMapping + < void /* deduce subview type from source view traits */ + , ViewTraits< D , P ... > + , Args ... > + ::template apply< MemoryTraits > + ::type( src , args ... ); } -template< class D, class A1, class A2, class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 > -KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 , T5 const & arg5 - ) -{ - typedef View< D, A1, A2, A3 > SrcView ; - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 6 , "Subview of rank 6 View requires 6 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, arg2, arg3, arg4, arg5, 0, 0 ); - - return dst ; -} - -template< class D, class A1, class A2, class A3 - , class T0 , class T1 , class T2 , class T3 - , class T4 > -KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - , T4 const & arg4 - ) -{ - typedef View< D, A1, A2, A3 > SrcView ; - - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 5 , "Subview of rank 5 View requires 5 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, arg2, arg3, arg4, 0, 0, 0 ); - - return dst ; -} - -template< class D, class A1, class A2, class A3 - , class T0 , class T1 , class T2 , class T3 > -KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 , T3 const & arg3 - ) -{ - typedef View< D, A1, A2, A3 > SrcView ; - - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 4 , "Subview of rank 4 View requires 4 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, arg2, arg3, 0, 0, 0, 0 ); - - return dst ; -} - -template< class D, class A1, class A2, class A3 - , class T0 , class T1 , class T2 > -KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 , T2 const & arg2 - ) -{ - typedef View< D, A1, A2, A3 > SrcView ; - - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 3 , "Subview of rank 3 View requires 3 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, arg2, 0, 0, 0, 0, 0 ); - - return dst ; -} - -template< class D, class A1, class A2, class A3 - , class T0 , class T1 > -KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src - , T0 const & arg0 , T1 const & arg1 - ) -{ - typedef View< D, A1, A2, A3 > SrcView ; - - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 2 , "Subview of rank 2 View requires 2 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map, src.m_map, arg0, arg1, 0, 0, 0, 0, 0, 0 ); - - return dst ; -} - -template< class D, class A1, class A2, class A3, class T0 > -KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::Subview< View< D, A1, A2, A3 > - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > -subview( const View< D, A1, A2, A3 > & src , T0 const & arg0 ) -{ - typedef View< D, A1, A2, A3 > SrcView ; - - typedef Kokkos::Experimental::Impl::SubviewMapping - < typename SrcView::traits - , Kokkos::Experimental::Impl::ViewOffsetRange::is_range - > Mapping ; - - typedef typename Mapping::type DstView ; - - static_assert( SrcView::Rank == 1 , "Subview of rank 1 View requires 1 arguments" ); - - DstView dst( src.m_track ); - - Mapping::assign( dst.m_map , src.m_map , arg0, 0, 0, 0, 0, 0, 0, 0 ); - - return dst ; -} } /* namespace Experimental */ } /* namespace Kokkos */ @@ -1490,15 +1287,14 @@ subview( const View< D, A1, A2, A3 > & src , T0 const & arg0 ) namespace Kokkos { namespace Experimental { -template< class LT , class L1 , class L2 , class L3 - , class RT , class R1 , class R2 , class R3 > +template< class LT , class ... LP , class RT , class ... RP > KOKKOS_INLINE_FUNCTION -bool operator == ( const View & lhs , - const View & rhs ) +bool operator == ( const View & lhs , + const View & rhs ) { // Same data, layout, dimensions - typedef ViewTraits lhs_traits ; - typedef ViewTraits rhs_traits ; + typedef ViewTraits lhs_traits ; + typedef ViewTraits rhs_traits ; return std::is_same< typename lhs_traits::const_value_type , @@ -1507,7 +1303,7 @@ bool operator == ( const View & lhs , typename rhs_traits::array_layout >::value && std::is_same< typename lhs_traits::memory_space , typename rhs_traits::memory_space >::value && - lhs_traits::Rank == rhs_traits::Rank && + lhs_traits::rank == rhs_traits::rank && lhs.data() == rhs.data() && lhs.span() == rhs.span() && lhs.dimension_0() == rhs.dimension_0() && @@ -1520,11 +1316,10 @@ bool operator == ( const View & lhs , lhs.dimension_7() == rhs.dimension_7(); } -template< class LT , class L1 , class L2 , class L3 - , class RT , class R1 , class R2 , class R3 > +template< class LT , class ... LP , class RT , class ... RP > KOKKOS_INLINE_FUNCTION -bool operator != ( const View & lhs , - const View & rhs ) +bool operator != ( const View & lhs , + const View & rhs ) { return ! ( operator==(lhs,rhs) ); } @@ -1535,6 +1330,37 @@ bool operator != ( const View & lhs , //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +namespace Kokkos { +namespace Impl { + +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + +inline +void shared_allocation_tracking_claim_and_disable() +{ Kokkos::Experimental::Impl::SharedAllocationRecord::tracking_claim_and_disable(); } + +inline +void shared_allocation_tracking_release_and_enable() +{ Kokkos::Experimental::Impl::SharedAllocationRecord::tracking_release_and_enable(); } + +#else + +inline +void shared_allocation_tracking_claim_and_disable() +{ Kokkos::Impl::AllocationTracker::disable_tracking(); } + +inline +void shared_allocation_tracking_release_and_enable() +{ Kokkos::Impl::AllocationTracker::enable_tracking(); } + +#endif + +} /* namespace Impl */ +} /* namespace Kokkos */ + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + namespace Kokkos { namespace Experimental { namespace Impl { @@ -1575,7 +1401,9 @@ struct ViewFill { typedef typename OutputView::execution_space execution_space ; typedef Kokkos::RangePolicy< execution_space > Policy ; - (void) Kokkos::Impl::ParallelFor< ViewFill , Policy >( *this , Policy( 0 , output.dimension_0() ) ); + const Kokkos::Impl::ParallelFor< ViewFill , Policy > closure( *this , Policy( 0 , output.dimension_0() ) ); + + closure.execute(); execution_space::fence(); } @@ -1617,7 +1445,8 @@ struct ViewRemap { { typedef typename OutputView::execution_space execution_space ; typedef Kokkos::RangePolicy< execution_space > Policy ; - (void) Kokkos::Impl::ParallelFor< ViewRemap , Policy >( *this , Policy( 0 , n0 ) ); + const Kokkos::Impl::ParallelFor< ViewRemap , Policy > closure( *this , Policy( 0 , n0 ) ); + closure.execute(); } KOKKOS_INLINE_FUNCTION @@ -1646,49 +1475,62 @@ namespace Kokkos { namespace Experimental { /** \brief Deep copy a value from Host memory into a view. */ -template< class DT , class D1 , class D2 , class D3 > +template< class DT , class ... DP > inline -void deep_copy( const View & dst - , typename ViewTraits::const_value_type & value ) +void deep_copy + ( const View & dst + , typename ViewTraits::const_value_type & value + , typename std::enable_if< + std::is_same< typename ViewTraits::specialize , void >::value + >::type * = 0 ) { - static_assert( std::is_same< typename ViewTraits::non_const_value_type , - typename ViewTraits::value_type >::value - , "ERROR: Incompatible deep_copy( View , value )" ); + static_assert( + std::is_same< typename ViewTraits::non_const_value_type , + typename ViewTraits::value_type >::value + , "deep_copy requires non-const type" ); - Kokkos::Experimental::Impl::ViewFill< View >( dst , value ); + Kokkos::Experimental::Impl::ViewFill< View >( dst , value ); } /** \brief Deep copy into a value in Host memory from a view. */ -template< class ST , class S1 , class S2 , class S3 > +template< class ST , class ... SP > inline -void deep_copy( ST & dst , const View & src ) +void deep_copy + ( ST & dst + , const View & src + , typename std::enable_if< + std::is_same< typename ViewTraits::specialize , void >::value + >::type * = 0 ) { - static_assert( ViewTraits::rank == 0 + static_assert( ViewTraits::rank == 0 , "ERROR: Non-rank-zero view in deep_copy( value , View )" ); - typedef ViewTraits src_traits ; + typedef ViewTraits src_traits ; typedef typename src_traits::memory_space src_memory_space ; Kokkos::Impl::DeepCopy< HostSpace , src_memory_space >( & dst , src.data() , sizeof(ST) ); } //---------------------------------------------------------------------------- /** \brief A deep copy between views of compatible type, and rank zero. */ -template< class DT , class D1 , class D2 , class D3 - , class ST , class S1 , class S2 , class S3 > +template< class DT , class ... DP , class ST , class ... SP > inline -void deep_copy( const View & dst , - const View & src , - typename std::enable_if<( - // Rank zero: - ( unsigned(ViewTraits::rank) == unsigned(0) ) && - ( unsigned(ViewTraits::rank) == unsigned(0) ) && - // Same type and destination is not constant: - std::is_same< typename ViewTraits::value_type , - typename ViewTraits::non_const_value_type >::value - )>::type * = 0 ) +void deep_copy + ( const View & dst + , const View & src + , typename std::enable_if<( + std::is_same< typename ViewTraits::specialize , void >::value && + std::is_same< typename ViewTraits::specialize , void >::value && + ( unsigned(ViewTraits::rank) == unsigned(0) && + unsigned(ViewTraits::rank) == unsigned(0) ) + )>::type * = 0 ) { - typedef View dst_type ; - typedef View src_type ; + static_assert( + std::is_same< typename ViewTraits::value_type , + typename ViewTraits::non_const_value_type >::value + , "deep_copy requires matching non-const destination type" ); + + typedef View dst_type ; + typedef View src_type ; typedef typename dst_type::value_type value_type ; typedef typename dst_type::memory_space dst_memory_space ; @@ -1703,30 +1545,30 @@ void deep_copy( const View & dst , /** \brief A deep copy between views of the default specialization, compatible type, * same non-zero rank, same contiguous layout. */ -template< class DT , class D1 , class D2 , class D3 , - class ST , class S1 , class S2 , class S3 > +template< class DT , class ... DP , class ST , class ... SP > inline -void deep_copy( const View & dst , - const View & src , - typename std::enable_if<( - // destination is non-const. - std::is_same< typename ViewTraits::value_type , - typename ViewTraits::non_const_value_type >::value - && - // Same non-zero rank: - ( unsigned(ViewTraits::rank) != 0 ) - && - ( unsigned(ViewTraits::rank) == - unsigned(ViewTraits::rank) ) - && - // Not specialized, default ViewMapping - std::is_same< typename ViewTraits::specialize , void >::value - && - std::is_same< typename ViewTraits::specialize , void >::value - )>::type * = 0 ) +void deep_copy + ( const View & dst + , const View & src + , typename std::enable_if<( + std::is_same< typename ViewTraits::specialize , void >::value && + std::is_same< typename ViewTraits::specialize , void >::value && + ( unsigned(ViewTraits::rank) != 0 || + unsigned(ViewTraits::rank) != 0 ) + )>::type * = 0 ) { - typedef View dst_type ; - typedef View src_type ; + static_assert( + std::is_same< typename ViewTraits::value_type , + typename ViewTraits::non_const_value_type >::value + , "deep_copy requires non-const destination type" ); + + static_assert( + ( unsigned(ViewTraits::rank) == + unsigned(ViewTraits::rank) ) + , "deep_copy requires Views of equal rank" ); + + typedef View dst_type ; + typedef View src_type ; typedef typename dst_type::execution_space dst_execution_space ; typedef typename dst_type::memory_space dst_memory_space ; @@ -1742,10 +1584,10 @@ void deep_copy( const View & dst , // If same type, equal layout, equal dimensions, equal span, and contiguous memory then can byte-wise copy - if ( std::is_same< typename ViewTraits::value_type , - typename ViewTraits::non_const_value_type >::value && - std::is_same< typename ViewTraits::array_layout , - typename ViewTraits::array_layout >::value && + if ( std::is_same< typename ViewTraits::value_type , + typename ViewTraits::non_const_value_type >::value && + std::is_same< typename ViewTraits::array_layout , + typename ViewTraits::array_layout >::value && dst.span_is_contiguous() && src.span_is_contiguous() && dst.span() == src.span() && @@ -1781,17 +1623,17 @@ void deep_copy( const View & dst , namespace Kokkos { namespace Experimental { -template< class T , class A1, class A2, class A3 > +template< class T , class ... P > inline -typename Kokkos::Experimental::View::HostMirror -create_mirror( const Kokkos::Experimental::View & src +typename Kokkos::Experimental::View::HostMirror +create_mirror( const Kokkos::Experimental::View & src , typename std::enable_if< - ! std::is_same< typename Kokkos::Experimental::ViewTraits::array_layout + ! std::is_same< typename Kokkos::Experimental::ViewTraits::array_layout , Kokkos::LayoutStride >::value >::type * = 0 ) { - typedef View src_type ; + typedef View src_type ; typedef typename src_type::HostMirror dst_type ; return dst_type( std::string( src.label() ).append("_mirror") @@ -1805,17 +1647,17 @@ create_mirror( const Kokkos::Experimental::View & src , src.dimension_7() ); } -template< class T , class A1, class A2, class A3 > +template< class T , class ... P > inline -typename Kokkos::Experimental::View::HostMirror -create_mirror( const Kokkos::Experimental::View & src +typename Kokkos::Experimental::View::HostMirror +create_mirror( const Kokkos::Experimental::View & src , typename std::enable_if< - std::is_same< typename Kokkos::Experimental::ViewTraits::array_layout + std::is_same< typename Kokkos::Experimental::ViewTraits::array_layout , Kokkos::LayoutStride >::value >::type * = 0 ) { - typedef View src_type ; + typedef View src_type ; typedef typename src_type::HostMirror dst_type ; Kokkos::LayoutStride layout ; @@ -1841,17 +1683,17 @@ create_mirror( const Kokkos::Experimental::View & src return dst_type( std::string( src.label() ).append("_mirror") , layout ); } -template< class T , class A1 , class A2 , class A3 > +template< class T , class ... P > inline -typename Kokkos::Experimental::View::HostMirror -create_mirror_view( const Kokkos::Experimental::View & src +typename Kokkos::Experimental::View::HostMirror +create_mirror_view( const Kokkos::Experimental::View & src , typename std::enable_if<( - std::is_same< typename Kokkos::Experimental::View::memory_space - , typename Kokkos::Experimental::View::HostMirror::memory_space + std::is_same< typename Kokkos::Experimental::View::memory_space + , typename Kokkos::Experimental::View::HostMirror::memory_space >::value && - std::is_same< typename Kokkos::Experimental::View::data_type - , typename Kokkos::Experimental::View::HostMirror::data_type + std::is_same< typename Kokkos::Experimental::View::data_type + , typename Kokkos::Experimental::View::HostMirror::data_type >::value )>::type * = 0 ) @@ -1859,17 +1701,17 @@ create_mirror_view( const Kokkos::Experimental::View & src return src ; } -template< class T , class A1 , class A2 , class A3 > +template< class T , class ... P > inline -typename Kokkos::Experimental::View::HostMirror -create_mirror_view( const Kokkos::Experimental::View & src +typename Kokkos::Experimental::View::HostMirror +create_mirror_view( const Kokkos::Experimental::View & src , typename std::enable_if< ! ( - std::is_same< typename Kokkos::Experimental::View::memory_space - , typename Kokkos::Experimental::View::HostMirror::memory_space + std::is_same< typename Kokkos::Experimental::View::memory_space + , typename Kokkos::Experimental::View::HostMirror::memory_space >::value && - std::is_same< typename Kokkos::Experimental::View::data_type - , typename Kokkos::Experimental::View::HostMirror::data_type + std::is_same< typename Kokkos::Experimental::View::data_type + , typename Kokkos::Experimental::View::HostMirror::data_type >::value )>::type * = 0 ) @@ -1887,9 +1729,9 @@ namespace Kokkos { namespace Experimental { /** \brief Resize a view with copying old data to new data at the corresponding indices. */ -template< class T , class A1 , class A2 , class A3 > +template< class T , class ... P > inline -void resize( Kokkos::Experimental::View & v , +void resize( Kokkos::Experimental::View & v , const size_t n0 = 0 , const size_t n1 = 0 , const size_t n2 = 0 , @@ -1899,9 +1741,9 @@ void resize( Kokkos::Experimental::View & v , const size_t n6 = 0 , const size_t n7 = 0 ) { - typedef Kokkos::Experimental::View view_type ; + typedef Kokkos::Experimental::View view_type ; - static_assert( Kokkos::Experimental::ViewTraits::is_managed , "Can only resize managed views" ); + static_assert( Kokkos::Experimental::ViewTraits::is_managed , "Can only resize managed views" ); view_type v_resized( v.label(), n0, n1, n2, n3, n4, n5, n6, n7 ); @@ -1911,9 +1753,9 @@ void resize( Kokkos::Experimental::View & v , } /** \brief Resize a view with copying old data to new data at the corresponding indices. */ -template< class T , class A1 , class A2 , class A3 > +template< class T , class ... P > inline -void realloc( Kokkos::Experimental::View & v , +void realloc( Kokkos::Experimental::View & v , const size_t n0 = 0 , const size_t n1 = 0 , const size_t n2 = 0 , @@ -1923,9 +1765,9 @@ void realloc( Kokkos::Experimental::View & v , const size_t n6 = 0 , const size_t n7 = 0 ) { - typedef Kokkos::Experimental::View view_type ; + typedef Kokkos::Experimental::View view_type ; - static_assert( Kokkos::Experimental::ViewTraits::is_managed , "Can only realloc managed views" ); + static_assert( Kokkos::Experimental::ViewTraits::is_managed , "Can only realloc managed views" ); const std::string label = v.label(); @@ -1943,18 +1785,20 @@ void realloc( Kokkos::Experimental::View & v , namespace Kokkos { -template< class D , class A1 = void , class A2 = void , class A3 = void > -using ViewTraits = Kokkos::Experimental::ViewTraits ; +template< class D , class ... P > +using ViewTraits = Kokkos::Experimental::ViewTraits ; -template< class D , class A1 = void , class A2 = void , class A3 = void , class S = void > -using View = Kokkos::Experimental::View ; +template< class D , class ... P > +using View = Kokkos::Experimental::View ; +using Kokkos::Experimental::ALL ; using Kokkos::Experimental::deep_copy ; using Kokkos::Experimental::create_mirror ; using Kokkos::Experimental::create_mirror_view ; using Kokkos::Experimental::subview ; using Kokkos::Experimental::resize ; using Kokkos::Experimental::realloc ; +using Kokkos::Experimental::is_view ; namespace Impl { diff --git a/lib/kokkos/core/src/Kokkos_Array.hpp b/lib/kokkos/core/src/Kokkos_Array.hpp index 7fe8b1c394..80a388901a 100644 --- a/lib/kokkos/core/src/Kokkos_Array.hpp +++ b/lib/kokkos/core/src/Kokkos_Array.hpp @@ -45,6 +45,7 @@ #define KOKKOS_ARRAY #include +#include #include namespace Kokkos { diff --git a/lib/kokkos/core/src/Kokkos_Complex.hpp b/lib/kokkos/core/src/Kokkos_Complex.hpp new file mode 100644 index 0000000000..11aaf96177 --- /dev/null +++ b/lib/kokkos/core/src/Kokkos_Complex.hpp @@ -0,0 +1,529 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 2.0 +// Copyright (2014) Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +// 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. +// +// Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ +#ifndef KOKKOS_COMPLEX_HPP +#define KOKKOS_COMPLEX_HPP + +#include +#include +#include + +namespace Kokkos { + +/// \class complex +/// \brief Partial reimplementation of std::complex that works as the +/// result of a Kokkos::parallel_reduce. +/// \tparam RealType The type of the real and imaginary parts of the +/// complex number. As with std::complex, this is only defined for +/// \c float, \c double, and long double. The latter is +/// currently forbidden in CUDA device kernels. +template +class complex { +private: + RealType re_, im_; + +public: + //! The type of the real or imaginary parts of this complex number. + typedef RealType value_type; + + //! Default constructor (initializes both real and imaginary parts to zero). + KOKKOS_INLINE_FUNCTION complex () : + re_ (0.0), im_ (0.0) + {} + + //! Copy constructor. + KOKKOS_INLINE_FUNCTION complex (const complex& src) : + re_ (src.re_), im_ (src.im_) + {} + + //! Copy constructor from volatile. + KOKKOS_INLINE_FUNCTION complex (const volatile complex& src) : + re_ (src.re_), im_ (src.im_) + {} + + /// \brief Conversion constructor from std::complex. + /// + /// This constructor cannot be called in a CUDA device function, + /// because std::complex's methods and nonmember functions are not + /// marked as CUDA device functions. + template + complex (const std::complex& src) : + re_ (std::real (src)), im_ (std::imag (src)) + {} + + /// \brief Conversion operator to std::complex. + /// + /// This operator cannot be called in a CUDA device function, + /// because std::complex's methods and nonmember functions are not + /// marked as CUDA device functions. + operator std::complex () const { + return std::complex (re_, im_); + } + + /// \brief Constructor that takes just the real part, and sets the + /// imaginary part to zero. + template + KOKKOS_INLINE_FUNCTION complex (const InputRealType& val) : + re_ (val), im_ (0.0) + {} + + //! Constructor that takes the real and imaginary parts. + template + KOKKOS_INLINE_FUNCTION complex (const RealType1& re, const RealType2& im) : + re_ (re), im_ (im) + {} + + //! Assignment operator. + template + KOKKOS_INLINE_FUNCTION + complex& operator= (const complex& src) { + re_ = src.re_; + im_ = src.im_; + return *this; + } + + //! Assignment operator. + template + KOKKOS_INLINE_FUNCTION + volatile complex& operator= (const complex& src) volatile { + re_ = src.re_; + im_ = src.im_; + return *this; + } + + //! Assignment operator. + template + KOKKOS_INLINE_FUNCTION + volatile complex& operator= (const volatile complex& src) volatile { + re_ = src.re_; + im_ = src.im_; + return *this; + } + + //! Assignment operator. + template + KOKKOS_INLINE_FUNCTION + complex& operator= (const volatile complex& src) { + re_ = src.re_; + im_ = src.im_; + return *this; + } + + //! Assignment operator (from a real number). + template + KOKKOS_INLINE_FUNCTION + complex& operator= (const InputRealType& val) { + re_ = val; + im_ = static_cast (0.0); + return *this; + } + + //! Assignment operator (from a real number). + template + KOKKOS_INLINE_FUNCTION + void operator= (const InputRealType& val) volatile { + re_ = val; + im_ = static_cast (0.0); + } + + /// \brief Assignment operator from std::complex. + /// + /// This constructor cannot be called in a CUDA device function, + /// because std::complex's methods and nonmember functions are not + /// marked as CUDA device functions. + template + complex& operator= (const std::complex& src) { + re_ = std::real (src); + im_ = std::imag (src); + return *this; + } + + //! The imaginary part of this complex number. + KOKKOS_INLINE_FUNCTION RealType& imag () { + return im_; + } + + //! The real part of this complex number. + KOKKOS_INLINE_FUNCTION RealType& real () { + return re_; + } + + //! The imaginary part of this complex number. + KOKKOS_INLINE_FUNCTION const RealType imag () const { + return im_; + } + + //! The real part of this complex number. + KOKKOS_INLINE_FUNCTION const RealType real () const { + return re_; + } + + //! The imaginary part of this complex number (volatile overload). + KOKKOS_INLINE_FUNCTION volatile RealType& imag () volatile { + return im_; + } + + //! The real part of this complex number (volatile overload). + KOKKOS_INLINE_FUNCTION volatile RealType& real () volatile { + return re_; + } + + //! The imaginary part of this complex number (volatile overload). + KOKKOS_INLINE_FUNCTION const RealType imag () const volatile { + return im_; + } + + //! The real part of this complex number (volatile overload). + KOKKOS_INLINE_FUNCTION const RealType real () const volatile { + return re_; + } + + KOKKOS_INLINE_FUNCTION + complex& operator += (const complex& src) { + re_ += src.re_; + im_ += src.im_; + return *this; + } + + KOKKOS_INLINE_FUNCTION + void operator += (const volatile complex& src) volatile { + re_ += src.re_; + im_ += src.im_; + } + + KOKKOS_INLINE_FUNCTION + complex& operator += (const RealType& src) { + re_ += src; + return *this; + } + + KOKKOS_INLINE_FUNCTION + void operator += (const volatile RealType& src) volatile { + re_ += src; + } + + KOKKOS_INLINE_FUNCTION + complex& operator -= (const complex& src) { + re_ -= src.re_; + im_ -= src.im_; + return *this; + } + + KOKKOS_INLINE_FUNCTION + complex& operator -= (const RealType& src) { + re_ -= src; + return *this; + } + + KOKKOS_INLINE_FUNCTION + complex& operator *= (const complex& src) { + const RealType realPart = re_ * src.re_ - im_ * src.im_; + const RealType imagPart = re_ * src.im_ + im_ * src.re_; + re_ = realPart; + im_ = imagPart; + return *this; + } + + KOKKOS_INLINE_FUNCTION + void operator *= (const volatile complex& src) volatile { + const RealType realPart = re_ * src.re_ - im_ * src.im_; + const RealType imagPart = re_ * src.im_ + im_ * src.re_; + re_ = realPart; + im_ = imagPart; + } + + KOKKOS_INLINE_FUNCTION + complex& operator *= (const RealType& src) { + re_ *= src; + im_ *= src; + return *this; + } + + KOKKOS_INLINE_FUNCTION + void operator *= (const volatile RealType& src) volatile { + re_ *= src; + im_ *= src; + } + + KOKKOS_INLINE_FUNCTION + complex& operator /= (const complex& y) { + // Scale (by the "1-norm" of y) to avoid unwarranted overflow. + // If the real part is +/-Inf and the imaginary part is -/+Inf, + // this won't change the result. + const RealType s = ::fabs (y.real ()) + ::fabs (y.imag ()); + + // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0. + // In that case, the relation x/y == (x/s) / (y/s) doesn't hold, + // because y/s is NaN. + if (s == 0.0) { + this->re_ /= s; + this->im_ /= s; + } + else { + const complex x_scaled (this->re_ / s, this->im_ / s); + const complex y_conj_scaled (y.re_ / s, -(y.im_) / s); + const RealType y_scaled_abs = y_conj_scaled.re_ * y_conj_scaled.re_ + + y_conj_scaled.im_ * y_conj_scaled.im_; // abs(y) == abs(conj(y)) + *this = x_scaled * y_conj_scaled; + *this /= y_scaled_abs; + } + return *this; + } + + KOKKOS_INLINE_FUNCTION + complex& operator /= (const RealType& src) { + re_ /= src; + im_ /= src; + return *this; + } +}; + +//! Binary + operator for complex. +template +KOKKOS_INLINE_FUNCTION +complex +operator + (const complex& x, const complex& y) { + return complex (x.real () + y.real (), x.imag () + y.imag ()); +} + +//! Unary + operator for complex. +template +KOKKOS_INLINE_FUNCTION +complex +operator + (const complex& x) { + return x; +} + +//! Binary - operator for complex. +template +KOKKOS_INLINE_FUNCTION +complex +operator - (const complex& x, const complex& y) { + return complex (x.real () - y.real (), x.imag () - y.imag ()); +} + +//! Unary - operator for complex. +template +KOKKOS_INLINE_FUNCTION +complex +operator - (const complex& x) { + return complex (-x.real (), -x.imag ()); +} + +//! Binary * operator for complex. +template +KOKKOS_INLINE_FUNCTION +complex +operator * (const complex& x, const complex& y) { + return complex (x.real () * y.real () - x.imag () * y.imag (), + x.real () * y.imag () + x.imag () * y.real ()); +} + +/// \brief Binary * operator for std::complex and complex. +/// +/// This function exists because GCC 4.7.2 (and perhaps other +/// compilers) are not able to deduce that they can multiply +/// std::complex by Kokkos::complex, by first converting std::complex +/// to Kokkos::complex. +/// +/// This function cannot be called in a CUDA device function, because +/// std::complex's methods and nonmember functions are not marked as +/// CUDA device functions. +template +complex +operator * (const std::complex& x, const complex& y) { + return complex (x.real () * y.real () - x.imag () * y.imag (), + x.real () * y.imag () + x.imag () * y.real ()); +} + +/// \brief Binary * operator for RealType times complex. +/// +/// This function exists because the compiler doesn't know that +/// RealType and complex commute with respect to operator*. +template +KOKKOS_INLINE_FUNCTION +complex +operator * (const RealType& x, const complex& y) { + return complex (x * y.real (), x * y.imag ()); +} + + +//! Imaginary part of a complex number. +template +KOKKOS_INLINE_FUNCTION +RealType imag (const complex& x) { + return x.imag (); +} + +//! Real part of a complex number. +template +KOKKOS_INLINE_FUNCTION +RealType real (const complex& x) { + return x.real (); +} + +//! Absolute value (magnitude) of a complex number. +template +KOKKOS_INLINE_FUNCTION +RealType abs (const complex& x) { + // FIXME (mfh 31 Oct 2014) Scale to avoid unwarranted overflow. + return ::sqrt (real (x) * real (x) + imag (x) * imag (x)); +} + +//! Conjugate of a complex number. +template +KOKKOS_INLINE_FUNCTION +complex conj (const complex& x) { + return complex (real (x), -imag (x)); +} + + +//! Binary operator / for complex and real numbers +template +KOKKOS_INLINE_FUNCTION +complex +operator / (const complex& x, const RealType2& y) { + return complex (real (x) / y, imag (x) / y); +} + +//! Binary operator / for complex. +template +KOKKOS_INLINE_FUNCTION +complex +operator / (const complex& x, const complex& y) { + // Scale (by the "1-norm" of y) to avoid unwarranted overflow. + // If the real part is +/-Inf and the imaginary part is -/+Inf, + // this won't change the result. + const RealType s = ::fabs (real (y)) + ::fabs (imag (y)); + + // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0. + // In that case, the relation x/y == (x/s) / (y/s) doesn't hold, + // because y/s is NaN. + if (s == 0.0) { + return complex (real (x) / s, imag (x) / s); + } + else { + const complex x_scaled (real (x) / s, imag (x) / s); + const complex y_conj_scaled (real (y) / s, -imag (y) / s); + const RealType y_scaled_abs = real (y_conj_scaled) * real (y_conj_scaled) + + imag (y_conj_scaled) * imag (y_conj_scaled); // abs(y) == abs(conj(y)) + complex result = x_scaled * y_conj_scaled; + result /= y_scaled_abs; + return result; + } +} + +//! Equality operator for two complex numbers. +template +KOKKOS_INLINE_FUNCTION +bool operator == (const complex& x, const complex& y) { + return real (x) == real (y) && imag (x) == imag (y); +} + +//! Equality operator for std::complex and Kokkos::complex. +template +KOKKOS_INLINE_FUNCTION +bool operator == (const std::complex& x, const complex& y) { + return std::real (x) == real (y) && std::imag (x) == imag (y); +} + +//! Equality operator for complex and real number. +template +KOKKOS_INLINE_FUNCTION +bool operator == (const complex& x, const RealType2& y) { + return real (x) == y && imag (x) == static_cast (0.0); +} + +//! Equality operator for real and complex number. +template +KOKKOS_INLINE_FUNCTION +bool operator == (const RealType& x, const complex& y) { + return y == x; +} + +//! Inequality operator for two complex numbers. +template +KOKKOS_INLINE_FUNCTION +bool operator != (const complex& x, const complex& y) { + return real (x) != real (y) || imag (x) != imag (y); +} + +//! Inequality operator for std::complex and Kokkos::complex. +template +KOKKOS_INLINE_FUNCTION +bool operator != (const std::complex& x, const complex& y) { + return std::real (x) != real (y) || std::imag (x) != imag (y); +} + +//! Inequality operator for complex and real number. +template +KOKKOS_INLINE_FUNCTION +bool operator != (const complex& x, const RealType2& y) { + return real (x) != y || imag (x) != static_cast (0.0); +} + +//! Inequality operator for real and complex number. +template +KOKKOS_INLINE_FUNCTION +bool operator != (const RealType& x, const complex& y) { + return y != x; +} + +template +std::ostream& operator << (std::ostream& os, const complex& x) { + const std::complex x_std (Kokkos::real (x), Kokkos::imag (x)); + os << x_std; + return os; +} + +template +std::ostream& operator >> (std::ostream& os, complex& x) { + std::complex x_std; + os >> x_std; + x = x_std; // only assigns on success of above + return os; +} + + +} // namespace Kokkos + +#endif // KOKKOS_COMPLEX_HPP diff --git a/lib/kokkos/core/src/Kokkos_Core.hpp b/lib/kokkos/core/src/Kokkos_Core.hpp index 2578313d77..ba4d2de15f 100644 --- a/lib/kokkos/core/src/Kokkos_Core.hpp +++ b/lib/kokkos/core/src/Kokkos_Core.hpp @@ -49,22 +49,22 @@ #include -#if defined( KOKKOS_HAVE_CUDA ) -#include +#if defined( KOKKOS_HAVE_SERIAL ) +#include #endif #if defined( KOKKOS_HAVE_OPENMP ) #include #endif -#if defined( KOKKOS_HAVE_SERIAL ) -#include -#endif - #if defined( KOKKOS_HAVE_PTHREAD ) #include #endif +#if defined( KOKKOS_HAVE_CUDA ) +#include +#endif + #include #include #include @@ -72,10 +72,8 @@ #include #include -#include - #ifdef KOKKOS_HAVE_CXX11 -////#include +#include #endif @@ -107,9 +105,70 @@ void finalize_all(); void fence(); +} // namespace Kokkos + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Experimental { + +/* Allocate memory from a memory space. + * The allocation is tracked in Kokkos memory tracking system, so + * leaked memory can be identified. + */ +template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > +inline +void * kokkos_malloc( const std::string & arg_alloc_label + , const size_t arg_alloc_size ) +{ + typedef typename Space::memory_space MemorySpace ; + return Impl::SharedAllocationRecord< MemorySpace >:: + allocate_tracked( MemorySpace() , arg_alloc_label , arg_alloc_size ); } -#ifdef KOKKOS_HAVE_CXX11 +template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > +inline +void * kokkos_malloc( const size_t arg_alloc_size ) +{ + typedef typename Space::memory_space MemorySpace ; + return Impl::SharedAllocationRecord< MemorySpace >:: + allocate_tracked( MemorySpace() , "no-label" , arg_alloc_size ); +} + +template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > +inline +void kokkos_free( void * arg_alloc ) +{ + typedef typename Space::memory_space MemorySpace ; + return Impl::SharedAllocationRecord< MemorySpace >:: + deallocate_tracked( arg_alloc ); +} + +template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > +inline +void * kokkos_realloc( void * arg_alloc , const size_t arg_alloc_size ) +{ + typedef typename Space::memory_space MemorySpace ; + return Impl::SharedAllocationRecord< MemorySpace >:: + reallocate_tracked( arg_alloc , arg_alloc_size ); +} + +} // namespace Experimental +} // namespace Kokkos + +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + +namespace Kokkos { + +using Kokkos::Experimental::kokkos_malloc ; +using Kokkos::Experimental::kokkos_realloc ; +using Kokkos::Experimental::kokkos_free ; + +} + +#else + namespace Kokkos { namespace Impl { @@ -161,7 +220,10 @@ void kokkos_free(const void* ptr) { template< class Arg = DefaultExecutionSpace> -const void* kokkos_realloc(const void* old_ptr, size_t size) { +void* kokkos_realloc(const void* old_ptr, size_t size) { + if(old_ptr == NULL) + return kokkos_malloc(size); + typedef typename Arg::memory_space MemorySpace; typedef typename MemorySpace::allocator allocator; Impl::AllocationTracker tracker = Impl::AllocationTracker::find(old_ptr); @@ -172,64 +234,11 @@ const void* kokkos_realloc(const void* old_ptr, size_t size) { } } // namespace Kokkos + #endif //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -namespace Kokkos { -namespace Experimental { - -template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > -inline -void * kokkos_malloc( const size_t arg_alloc_size ) -{ - typedef typename Space::memory_space MemorySpace ; - typedef Kokkos::Experimental::Impl::SharedAllocationRecord< void , void > RecordBase ; - typedef Kokkos::Experimental::Impl::SharedAllocationRecord< MemorySpace , void > RecordHost ; - - RecordHost * const r = RecordHost::allocate( MemorySpace() , "kokkos_malloc" , arg_alloc_size ); - - RecordBase::increment( r ); - - return r->data(); -} - -template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > -inline -void kokkos_free( void * arg_alloc ) -{ - typedef typename Space::memory_space MemorySpace ; - typedef Kokkos::Experimental::Impl::SharedAllocationRecord< void , void > RecordBase ; - typedef Kokkos::Experimental::Impl::SharedAllocationRecord< MemorySpace , void > RecordHost ; - - RecordHost * const r = RecordHost::get_record( arg_alloc ); - - RecordBase::decrement( r ); -} - -template< class Space = typename Kokkos::DefaultExecutionSpace::memory_space > -inline -void * kokkos_realloc( void * arg_alloc , const size_t arg_alloc_size ) -{ - typedef typename Space::memory_space MemorySpace ; - typedef Kokkos::Experimental::Impl::SharedAllocationRecord< void , void > RecordBase ; - typedef Kokkos::Experimental::Impl::SharedAllocationRecord< MemorySpace , void > RecordHost ; - - RecordHost * const r_old = RecordHost::get_record( arg_alloc ); - RecordHost * const r_new = RecordHost::allocate( MemorySpace() , "kokkos_malloc" , arg_alloc_size ); - - Kokkos::Impl::DeepCopy( r_new->data() , r_old->data() - , std::min( r_old->size() , r_new->size() ) ); - - RecordBase::increment( r_new ); - RecordBase::decrement( r_old ); - - return r_new->data(); -} - -} // namespace Experimental -} // namespace Kokkos - #endif diff --git a/lib/kokkos/core/src/Kokkos_Core_fwd.hpp b/lib/kokkos/core/src/Kokkos_Core_fwd.hpp index 2cde9299a4..7e18884218 100644 --- a/lib/kokkos/core/src/Kokkos_Core_fwd.hpp +++ b/lib/kokkos/core/src/Kokkos_Core_fwd.hpp @@ -50,6 +50,22 @@ #include +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { + +struct AUTO_t { + KOKKOS_INLINE_FUNCTION + constexpr const AUTO_t & operator()() const { return *this ; } +}; + +namespace { +/**\brief Token to indicate that a parameter's value is to be automatically selected */ +constexpr AUTO_t AUTO = Kokkos::AUTO_t(); +} +} + //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // Forward declarations for class inter-relationships @@ -58,6 +74,12 @@ namespace Kokkos { class HostSpace ; ///< Memory space for main process and CPU execution spaces +#ifdef KOKKOS_HAVE_HBWSPACE +namespace Experimental { +class HBWSpace ; /// Memory space for hbw_malloc from memkind (e.g. for KNL processor) +} +#endif + #if defined( KOKKOS_HAVE_SERIAL ) class Serial ; ///< Execution space main process on CPU #endif // defined( KOKKOS_HAVE_SERIAL ) @@ -162,9 +184,15 @@ struct VerifyExecutionCanAccessMemorySpace< Space , Space > Kokkos::Impl::VerifyExecutionCanAccessMemorySpace< \ Kokkos::Impl::ActiveExecutionMemorySpace , DATA_SPACE >::verify() +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + namespace Kokkos { void fence(); } +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + #endif /* #ifndef KOKKOS_CORE_FWD_HPP */ diff --git a/lib/kokkos/core/src/Kokkos_CudaSpace.hpp b/lib/kokkos/core/src/Kokkos_CudaSpace.hpp index 3caf250536..e6b337ecac 100644 --- a/lib/kokkos/core/src/Kokkos_CudaSpace.hpp +++ b/lib/kokkos/core/src/Kokkos_CudaSpace.hpp @@ -75,6 +75,10 @@ public: typedef unsigned int size_type ; + /*--------------------------------*/ + +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + typedef Impl::CudaMallocAllocator allocator; /** \brief Allocate a contiguous block of memory. @@ -96,6 +100,8 @@ public: ); #endif +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + /*--------------------------------*/ CudaSpace(); @@ -103,10 +109,10 @@ public: CudaSpace & operator = ( const CudaSpace & rhs ) = default ; ~CudaSpace() = default ; - /**\brief Allocate memory in the cuda space */ + /**\brief Allocate untracked memory in the cuda space */ void * allocate( const size_t arg_alloc_size ) const ; - /**\brief Deallocate memory in the cuda space */ + /**\brief Deallocate untracked memory in the cuda space */ void deallocate( void * const arg_alloc_ptr , const size_t arg_alloc_size ) const ; @@ -162,6 +168,10 @@ public: /** \brief If UVM capability is available */ static bool available(); + /*--------------------------------*/ + +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + typedef Impl::CudaUVMAllocator allocator; /** \brief Allocate a contiguous block of memory. @@ -182,6 +192,9 @@ public: , ::cudaChannelFormatDesc const & desc ); #endif + +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + /*--------------------------------*/ CudaUVMSpace(); @@ -189,10 +202,10 @@ public: CudaUVMSpace & operator = ( const CudaUVMSpace & rhs ) = default ; ~CudaUVMSpace() = default ; - /**\brief Allocate memory in the cuda space */ + /**\brief Allocate untracked memory in the cuda space */ void * allocate( const size_t arg_alloc_size ) const ; - /**\brief Deallocate memory in the cuda space */ + /**\brief Deallocate untracked memory in the cuda space */ void deallocate( void * const arg_alloc_ptr , const size_t arg_alloc_size ) const ; @@ -223,6 +236,9 @@ public: typedef Kokkos::Device device_type; typedef unsigned int size_type ; + /*--------------------------------*/ + +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) typedef Impl::CudaHostAllocator allocator ; @@ -234,6 +250,8 @@ public: */ static Impl::AllocationTracker allocate_and_track( const std::string & label, const size_t size ); +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + /*--------------------------------*/ CudaHostPinnedSpace(); @@ -241,10 +259,10 @@ public: CudaHostPinnedSpace & operator = ( const CudaHostPinnedSpace & rhs ) = default ; ~CudaHostPinnedSpace() = default ; - /**\brief Allocate memory in the cuda space */ + /**\brief Allocate untracked memory in the space */ void * allocate( const size_t arg_alloc_size ) const ; - /**\brief Deallocate memory in the cuda space */ + /**\brief Deallocate untracked memory in the space */ void deallocate( void * const arg_alloc_ptr , const size_t arg_alloc_size ) const ; @@ -631,8 +649,24 @@ public: static SharedAllocationRecord * allocate( const Kokkos::CudaSpace & arg_space , const std::string & arg_label - , const size_t arg_alloc_size - ); + , const size_t arg_alloc_size ); + + /**\brief Allocate tracked memory in the space */ + static + void * allocate_tracked( const Kokkos::CudaSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size ); + + /**\brief Reallocate tracked memory in the space */ + static + void * reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ); + + /**\brief Deallocate tracked memory in the space */ + static + void deallocate_tracked( void * const arg_alloc_ptr ); + + static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); template< typename AliasType > inline @@ -660,8 +694,6 @@ public: return ptr - reinterpret_cast( RecordBase::m_alloc_ptr ); } - static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); - static void print_records( std::ostream & , const Kokkos::CudaSpace & , bool detail = false ); }; @@ -704,6 +736,24 @@ public: , const size_t arg_alloc_size ); + /**\brief Allocate tracked memory in the space */ + static + void * allocate_tracked( const Kokkos::CudaUVMSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size ); + + /**\brief Reallocate tracked memory in the space */ + static + void * reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ); + + /**\brief Deallocate tracked memory in the space */ + static + void deallocate_tracked( void * const arg_alloc_ptr ); + + static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); + + template< typename AliasType > inline ::cudaTextureObject_t attach_texture_object() @@ -731,8 +781,6 @@ public: return ptr - reinterpret_cast( RecordBase::m_alloc_ptr ); } - static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); - static void print_records( std::ostream & , const Kokkos::CudaUVMSpace & , bool detail = false ); }; @@ -772,6 +820,21 @@ public: , const std::string & arg_label , const size_t arg_alloc_size ); + /**\brief Allocate tracked memory in the space */ + static + void * allocate_tracked( const Kokkos::CudaHostPinnedSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size ); + + /**\brief Reallocate tracked memory in the space */ + static + void * reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ); + + /**\brief Deallocate tracked memory in the space */ + static + void deallocate_tracked( void * const arg_alloc_ptr ); + static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); diff --git a/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp b/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp index 807cb5cb43..4f6f0f09c6 100644 --- a/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp +++ b/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp @@ -78,8 +78,9 @@ template< class Arg0 = void , class Arg1 = void , class Arg2 = void , class ExecSpace = // The first argument is the execution space, // otherwise use the default execution space. - typename Impl::if_c< Impl::is_execution_space< Arg0 >::value , Arg0 - , Kokkos::DefaultExecutionSpace >::type + typename std::conditional + < Impl::is_execution_space< Arg0 >::value , Arg0 + , Kokkos::DefaultExecutionSpace >::type > class RangePolicy { private: @@ -117,8 +118,8 @@ private: ) >::value }; // The work argument tag is the first or second argument - typedef typename Impl::if_c< Arg0_WorkTag , Arg0 , - typename Impl::if_c< Arg1_WorkTag , Arg1 , void + typedef typename std::conditional< Arg0_WorkTag , Arg0 , + typename std::conditional< Arg1_WorkTag , Arg1 , void >::type >::type WorkTag ; @@ -128,17 +129,18 @@ private: unsigned(DefaultIntValue) ))) }; // Only accept the integral type if the blocking is a power of two - typedef typename Impl::enable_if< Impl::is_power_of_two< Granularity >::value , - typename Impl::if_c< Arg0_IntType , Arg0 , - typename Impl::if_c< Arg1_IntType , Arg1 , - typename Impl::if_c< Arg2_IntType , Arg2 , - typename Impl::if_c< Arg0_IntConst , typename Impl::is_integral_constant::integral_type , - typename Impl::if_c< Arg1_IntConst , typename Impl::is_integral_constant::integral_type , - typename Impl::if_c< Arg2_IntConst , typename Impl::is_integral_constant::integral_type , - DefaultIntType - >::type >::type >::type - >::type >::type >::type - >::type + static_assert( Impl::is_integral_power_of_two( Granularity ) + , "RangePolicy blocking granularity must be power of two" ); + + typedef typename std::conditional< Arg0_IntType , Arg0 , + typename std::conditional< Arg1_IntType , Arg1 , + typename std::conditional< Arg2_IntType , Arg2 , + typename std::conditional< Arg0_IntConst , typename Impl::is_integral_constant::integral_type , + typename std::conditional< Arg1_IntConst , typename Impl::is_integral_constant::integral_type , + typename std::conditional< Arg2_IntConst , typename Impl::is_integral_constant::integral_type , + DefaultIntType + >::type >::type >::type + >::type >::type >::type IntType ; enum { GranularityMask = IntType(Granularity) - 1 }; @@ -187,8 +189,8 @@ public: * Typically used to partition a range over a group of threads. */ struct WorkRange { - typedef RangePolicy::work_tag work_tag ; - typedef RangePolicy::member_type member_type ; + typedef typename RangePolicy::work_tag work_tag ; + typedef typename RangePolicy::member_type member_type ; KOKKOS_INLINE_FUNCTION member_type begin() const { return m_begin ; } KOKKOS_INLINE_FUNCTION member_type end() const { return m_end ; } @@ -233,6 +235,38 @@ public: namespace Kokkos { +namespace Experimental { + +/** \brief Scratch memory request accepting per team and per thread value + * + * An instance of this class can be given as the last argument to a + * TeamPolicy constructor. It sets the amount of user requested shared + * memory for the team. + */ + +template< class MemorySpace > +class TeamScratchRequest { + size_t m_per_team; + size_t m_per_thread; + +public: + TeamScratchRequest(size_t per_team_, size_t per_thread_ = 0): + m_per_team(per_team_), m_per_thread(per_thread_) { + } + + size_t per_team() const { + return m_per_team; + } + size_t per_thread() const { + return m_per_thread; + } + size_t total(const size_t team_size) const { + return m_per_team + m_per_thread * team_size; + } +}; + +} + /** \brief Execution policy for parallel work over a league of teams of threads. * * The work functor is called for each thread of each team such that @@ -258,8 +292,9 @@ template< class Arg0 = void , class ExecSpace = // If the first argument is not an execution // then use the default execution space. - typename Impl::if_c< Impl::is_execution_space< Arg0 >::value , Arg0 - , Kokkos::DefaultExecutionSpace >::type + typename std::conditional + < Impl::is_execution_space< Arg0 >::value , Arg0 + , Kokkos::DefaultExecutionSpace >::type > class TeamPolicy { private: @@ -268,7 +303,7 @@ private: enum { Arg1_Void = Impl::is_same< Arg1 , void >::value }; enum { ArgOption_OK = Impl::StaticAssert< ( Arg0_ExecSpace || Arg1_Void ) >::value }; - typedef typename Impl::if_c< Arg0_ExecSpace , Arg1 , Arg0 >::type WorkTag ; + typedef typename std::conditional< Arg0_ExecSpace , Arg1 , Arg0 >::type WorkTag ; public: @@ -300,10 +335,20 @@ public: static int team_size_recommended( const FunctorType & , const int&); //---------------------------------------- /** \brief Construct policy with the given instance of the execution space */ - TeamPolicy( const execution_space & , int league_size_request , int team_size_request ); + TeamPolicy( const execution_space & , int league_size_request , int team_size_request , int vector_length_request = 1 ); + + TeamPolicy( const execution_space & , int league_size_request , const Kokkos::AUTO_t & , int vector_length_request = 1 ); /** \brief Construct policy with the default instance of the execution space */ - TeamPolicy( int league_size_request , int team_size_request ); + TeamPolicy( int league_size_request , int team_size_request , int vector_length_request = 1 ); + + TeamPolicy( int league_size_request , const Kokkos::AUTO_t & , int vector_length_request = 1 ); + + template + TeamPolicy( int league_size_request , int team_size_request , const Experimental::TeamScratchRequest& team_scratch_memory_request ); + + template + TeamPolicy( int league_size_request , const Kokkos::AUTO_t & , const Experimental::TeamScratchRequest& team_scratch_memory_request ); /** \brief The actual league size (number of teams) of the policy. * diff --git a/lib/kokkos/core/src/Kokkos_HBWSpace.hpp b/lib/kokkos/core/src/Kokkos_HBWSpace.hpp new file mode 100644 index 0000000000..94988e60b4 --- /dev/null +++ b/lib/kokkos/core/src/Kokkos_HBWSpace.hpp @@ -0,0 +1,327 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 2.0 +// Copyright (2014) Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +// 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. +// +// Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#ifndef KOKKOS_HBWSPACE_HPP +#define KOKKOS_HBWSPACE_HPP + + +#include +#include + +/*--------------------------------------------------------------------------*/ +#ifdef KOKKOS_HAVE_HBWSPACE + +namespace Kokkos { +namespace Experimental { +namespace Impl { + +/// \brief Initialize lock array for arbitrary size atomics. +/// +/// Arbitrary atomics are implemented using a hash table of locks +/// where the hash value is derived from the address of the +/// object for which an atomic operation is performed. +/// This function initializes the locks to zero (unset). +void init_lock_array_hbw_space(); + +/// \brief Aquire a lock for the address +/// +/// This function tries to aquire the lock for the hash value derived +/// from the provided ptr. If the lock is successfully aquired the +/// function returns true. Otherwise it returns false. +bool lock_address_hbw_space(void* ptr); + +/// \brief Release lock for the address +/// +/// This function releases the lock for the hash value derived +/// from the provided ptr. This function should only be called +/// after previously successfully aquiring a lock with +/// lock_address. +void unlock_address_hbw_space(void* ptr); + +} // namespace Impl +} // neamspace Experimental +} // namespace Kokkos + +namespace Kokkos { +namespace Experimental { + +/// \class HBWSpace +/// \brief Memory management for host memory. +/// +/// HBWSpace is a memory space that governs host memory. "Host" +/// memory means the usual CPU-accessible memory. +class HBWSpace { +public: + + //! Tag this class as a kokkos memory space + typedef HBWSpace memory_space ; + typedef size_t size_type ; + + /// \typedef execution_space + /// \brief Default execution space for this memory space. + /// + /// Every memory space has a default execution space. This is + /// useful for things like initializing a View (which happens in + /// parallel using the View's default execution space). +#if defined( KOKKOS_HAVE_DEFAULT_DEVICE_TYPE_OPENMP ) + typedef Kokkos::OpenMP execution_space ; +#elif defined( KOKKOS_HAVE_DEFAULT_DEVICE_TYPE_THREADS ) + typedef Kokkos::Threads execution_space ; +#elif defined( KOKKOS_HAVE_OPENMP ) + typedef Kokkos::OpenMP execution_space ; +#elif defined( KOKKOS_HAVE_PTHREAD ) + typedef Kokkos::Threads execution_space ; +#elif defined( KOKKOS_HAVE_SERIAL ) + typedef Kokkos::Serial execution_space ; +#else +# error "At least one of the following host execution spaces must be defined: Kokkos::OpenMP, Kokkos::Serial, or Kokkos::Threads. You might be seeing this message if you disabled the Kokkos::Serial device explicitly using the Kokkos_ENABLE_Serial:BOOL=OFF CMake option, but did not enable any of the other host execution space devices." +#endif + + //! This memory space preferred device_type + typedef Kokkos::Device device_type; + + /*--------------------------------*/ +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + + typedef Impl::HBWMallocAllocator allocator ; + + /** \brief Allocate a contiguous block of memory. + * + * The input label is associated with the block of memory. + * The block of memory is tracked via reference counting where + * allocation gives it a reference count of one. + */ + static Kokkos::Impl::AllocationTracker allocate_and_track( const std::string & label, const size_t size ); + +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + + /*--------------------------------*/ + /* Functions unique to the HBWSpace */ + static int in_parallel(); + + static void register_in_parallel( int (*)() ); + + /*--------------------------------*/ + + /**\brief Default memory space instance */ + HBWSpace(); + HBWSpace( const HBWSpace & rhs ) = default ; + HBWSpace & operator = ( const HBWSpace & ) = default ; + ~HBWSpace() = default ; + + /**\brief Non-default memory space instance to choose allocation mechansim, if available */ + + enum AllocationMechanism { STD_MALLOC , POSIX_MEMALIGN , POSIX_MMAP , INTEL_MM_ALLOC }; + + explicit + HBWSpace( const AllocationMechanism & ); + + /**\brief Allocate untracked memory in the space */ + void * allocate( const size_t arg_alloc_size ) const ; + + /**\brief Deallocate untracked memory in the space */ + void deallocate( void * const arg_alloc_ptr + , const size_t arg_alloc_size ) const ; + +private: + + AllocationMechanism m_alloc_mech ; + + friend class Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void > ; +}; + +} // namespace Experimental +} // namespace Kokkos + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Experimental { +namespace Impl { + +template<> +class SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void > + : public SharedAllocationRecord< void , void > +{ +private: + + friend Kokkos::Experimental::HBWSpace ; + + typedef SharedAllocationRecord< void , void > RecordBase ; + + SharedAllocationRecord( const SharedAllocationRecord & ) = delete ; + SharedAllocationRecord & operator = ( const SharedAllocationRecord & ) = delete ; + + static void deallocate( RecordBase * ); + + /**\brief Root record for tracked allocations from this HBWSpace instance */ + static RecordBase s_root_record ; + + const Kokkos::Experimental::HBWSpace m_space ; + +protected: + + ~SharedAllocationRecord(); + SharedAllocationRecord() = default ; + + SharedAllocationRecord( const Kokkos::Experimental::HBWSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size + , const RecordBase::function_type arg_dealloc = & deallocate + ); + +public: + + inline + std::string get_label() const + { + return std::string( RecordBase::head()->m_label ); + } + + KOKKOS_INLINE_FUNCTION static + SharedAllocationRecord * allocate( const Kokkos::Experimental::HBWSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size + ) + { +#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) + return new SharedAllocationRecord( arg_space , arg_label , arg_alloc_size ); +#else + return (SharedAllocationRecord *) 0 ; +#endif + } + + /**\brief Allocate tracked memory in the space */ + static + void * allocate_tracked( const Kokkos::Experimental::HBWSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size ); + + /**\brief Reallocate tracked memory in the space */ + static + void * reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ); + + /**\brief Deallocate tracked memory in the space */ + static + void deallocate_tracked( void * const arg_alloc_ptr ); + + + static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); + + static void print_records( std::ostream & , const Kokkos::Experimental::HBWSpace & , bool detail = false ); +}; + +} // namespace Impl +} // namespace Experimental +} // namespace Kokkos + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Impl { + + +template +struct DeepCopy { + DeepCopy( void * dst , const void * src , size_t n ) { + memcpy( dst , src , n ); + } + DeepCopy( const ExecutionSpace& exec, void * dst , const void * src , size_t n ) { + exec.fence(); + memcpy( dst , src , n ); + } +}; + +template +struct DeepCopy { + DeepCopy( void * dst , const void * src , size_t n ) { + memcpy( dst , src , n ); + } + DeepCopy( const ExecutionSpace& exec, void * dst , const void * src , size_t n ) { + exec.fence(); + memcpy( dst , src , n ); + } +}; + +template +struct DeepCopy { + DeepCopy( void * dst , const void * src , size_t n ) { + memcpy( dst , src , n ); + } + DeepCopy( const ExecutionSpace& exec, void * dst , const void * src , size_t n ) { + exec.fence(); + memcpy( dst , src , n ); + } +}; + +} // namespace Impl +} // namespace Kokkos + +namespace Kokkos { +namespace Impl { + +template<> +struct VerifyExecutionCanAccessMemorySpace< Kokkos::HostSpace , Kokkos::Experimental::HBWSpace > +{ + enum { value = true }; + inline static void verify( void ) { } + inline static void verify( const void * ) { } +}; + +template<> +struct VerifyExecutionCanAccessMemorySpace< Kokkos::Experimental::HBWSpace , Kokkos::HostSpace > +{ + enum { value = true }; + inline static void verify( void ) { } + inline static void verify( const void * ) { } +}; + +} // namespace Impl +} // namespace Kokkos + +#endif +#endif /* #define KOKKOS_HBWSPACE_HPP */ + diff --git a/lib/kokkos/core/src/Kokkos_HostSpace.hpp b/lib/kokkos/core/src/Kokkos_HostSpace.hpp index 2aa809e7ce..6e707f060c 100644 --- a/lib/kokkos/core/src/Kokkos_HostSpace.hpp +++ b/lib/kokkos/core/src/Kokkos_HostSpace.hpp @@ -128,6 +128,8 @@ public: //! This memory space preferred device_type typedef Kokkos::Device device_type; + /*--------------------------------*/ +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) #if defined( KOKKOS_USE_PAGE_ALIGNED_HOST_MEMORY ) typedef Impl::PageAlignedAllocator allocator ; @@ -143,6 +145,8 @@ public: */ static Impl::AllocationTracker allocate_and_track( const std::string & label, const size_t size ); +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + /*--------------------------------*/ /* Functions unique to the HostSpace */ static int in_parallel(); @@ -164,10 +168,10 @@ public: explicit HostSpace( const AllocationMechanism & ); - /**\brief Allocate memory in the host space */ + /**\brief Allocate untracked memory in the space */ void * allocate( const size_t arg_alloc_size ) const ; - /**\brief Deallocate memory in the host space */ + /**\brief Deallocate untracked memory in the space */ void deallocate( void * const arg_alloc_ptr , const size_t arg_alloc_size ) const ; @@ -239,6 +243,21 @@ public: #endif } + /**\brief Allocate tracked memory in the space */ + static + void * allocate_tracked( const Kokkos::HostSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size ); + + /**\brief Reallocate tracked memory in the space */ + static + void * reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ); + + /**\brief Deallocate tracked memory in the space */ + static + void deallocate_tracked( void * const arg_alloc_ptr ); + static SharedAllocationRecord * get_record( void * arg_alloc_ptr ); diff --git a/lib/kokkos/core/src/Kokkos_Layout.hpp b/lib/kokkos/core/src/Kokkos_Layout.hpp index 32822889df..e7d38a902b 100644 --- a/lib/kokkos/core/src/Kokkos_Layout.hpp +++ b/lib/kokkos/core/src/Kokkos_Layout.hpp @@ -157,10 +157,15 @@ struct LayoutStride { /// both tile dimensions are powers of two, Kokkos can optimize /// further. template < unsigned ArgN0 , unsigned ArgN1 , - bool IsPowerOfTwo = ( Impl::is_power_of_two::value && - Impl::is_power_of_two::value ) + bool IsPowerOfTwo = ( Impl::is_integral_power_of_two(ArgN0) && + Impl::is_integral_power_of_two(ArgN1) ) > struct LayoutTileLeft { + + static_assert( Impl::is_integral_power_of_two(ArgN0) && + Impl::is_integral_power_of_two(ArgN1) + , "LayoutTileLeft must be given power-of-two tile dimensions" ); + //! Tag this class as a kokkos array layout typedef LayoutTileLeft array_layout ; diff --git a/lib/kokkos/core/src/Kokkos_Macros.hpp b/lib/kokkos/core/src/Kokkos_Macros.hpp index c221c2f9f3..2386c9d2c3 100644 --- a/lib/kokkos/core/src/Kokkos_Macros.hpp +++ b/lib/kokkos/core/src/Kokkos_Macros.hpp @@ -416,5 +416,11 @@ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +#if ( defined( _POSIX_C_SOURCE ) && _POSIX_C_SOURCE >= 200112L ) || \ + ( defined( _XOPEN_SOURCE ) && _XOPEN_SOURCE >= 600 ) +#if defined(KOKKOS_ENABLE_PERFORMANCE_POSIX_MEMALIGN) +#define KOKKOS_POSIX_MEMALIGN_AVAILABLE 1 +#endif +#endif #endif /* #ifndef KOKKOS_MACROS_HPP */ diff --git a/lib/kokkos/core/src/Kokkos_MemoryTraits.hpp b/lib/kokkos/core/src/Kokkos_MemoryTraits.hpp index b581c7da23..5ee1f16fec 100644 --- a/lib/kokkos/core/src/Kokkos_MemoryTraits.hpp +++ b/lib/kokkos/core/src/Kokkos_MemoryTraits.hpp @@ -101,9 +101,9 @@ namespace Impl { */ enum { MEMORY_ALIGNMENT = #if defined( KOKKOS_MEMORY_ALIGNMENT ) - ( 1 << Kokkos::Impl::power_of_two< KOKKOS_MEMORY_ALIGNMENT >::value ) + ( 1 << Kokkos::Impl::integral_power_of_two( KOKKOS_MEMORY_ALIGNMENT ) ) #else - ( 1 << Kokkos::Impl::power_of_two< 128 >::value ) + ( 1 << Kokkos::Impl::integral_power_of_two( 128 ) ) #endif , MEMORY_ALIGNMENT_THRESHOLD = 4 }; diff --git a/lib/kokkos/core/src/Kokkos_OpenMP.hpp b/lib/kokkos/core/src/Kokkos_OpenMP.hpp index 508da04c87..e7dbf9a0e6 100644 --- a/lib/kokkos/core/src/Kokkos_OpenMP.hpp +++ b/lib/kokkos/core/src/Kokkos_OpenMP.hpp @@ -53,6 +53,9 @@ #include #include #include +#ifdef KOKKOS_HAVE_HBWSPACE +#include +#endif #include #include #include @@ -72,12 +75,16 @@ public: //! Tag this class as a kokkos execution space typedef OpenMP execution_space ; + #ifdef KOKKOS_HAVE_HBWSPACE + typedef Experimental::HBWSpace memory_space ; + #else typedef HostSpace memory_space ; + #endif //! This execution space preferred device_type typedef Kokkos::Device device_type; typedef LayoutRight array_layout ; - typedef HostSpace::size_type size_type ; + typedef memory_space::size_type size_type ; typedef ScratchMemorySpace< OpenMP > scratch_memory_space ; diff --git a/lib/kokkos/core/src/Kokkos_Parallel.hpp b/lib/kokkos/core/src/Kokkos_Parallel.hpp index 93bffcc781..696ff4042e 100644 --- a/lib/kokkos/core/src/Kokkos_Parallel.hpp +++ b/lib/kokkos/core/src/Kokkos_Parallel.hpp @@ -207,8 +207,12 @@ void parallel_for( const ExecPolicy & policy } #endif - (void) Impl::ParallelFor< FunctorType , ExecPolicy >( Impl::CopyWithoutTracking::apply(functor) , policy ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelFor< FunctorType , ExecPolicy > closure( functor , policy ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + closure.execute(); + #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { Kokkos::Experimental::endParallelFor(kpID); @@ -235,7 +239,11 @@ void parallel_for( const size_t work_count } #endif - (void) Impl::ParallelFor< FunctorType , policy >( Impl::CopyWithoutTracking::apply(functor) , policy(0,work_count) ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelFor< FunctorType , policy > closure( functor , policy(0,work_count) ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -333,7 +341,11 @@ void parallel_reduce( const ExecPolicy & policy } #endif - (void) Impl::ParallelReduce< FunctorType , ExecPolicy >( Impl::CopyWithoutTracking::apply(functor) , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType , ExecPolicy > closure( functor , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -376,7 +388,11 @@ void parallel_reduce( const size_t work_count } #endif - (void) Impl::ParallelReduce< FunctorType , policy >( Impl::CopyWithoutTracking::apply(functor) , policy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType , policy > closure( functor , policy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -394,7 +410,7 @@ void parallel_reduce( const ExecPolicy & policy , const ViewType & result_view , const std::string& str = "" , typename Impl::enable_if< - ( Impl::is_view::value && ! Impl::is_integral< ExecPolicy >::value + ( Kokkos::is_view::value && ! Impl::is_integral< ExecPolicy >::value #ifdef KOKKOS_HAVE_CUDA && ! Impl::is_same::value #endif @@ -408,7 +424,11 @@ void parallel_reduce( const ExecPolicy & policy } #endif - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( Impl::CopyWithoutTracking::apply(functor) , policy , Impl::CopyWithoutTracking::apply(result_view) ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( functor , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -465,7 +485,11 @@ void parallel_reduce( const ExecPolicy & policy } #endif - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( Impl::CopyWithoutTracking::apply(functor) , policy , Impl::CopyWithoutTracking::apply(result_view) ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( functor , policy , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -482,7 +506,7 @@ void parallel_reduce( const size_t work_count , const FunctorType & functor , const ViewType & result_view , const std::string& str = "" - , typename Impl::enable_if<( Impl::is_view::value + , typename Impl::enable_if<( Kokkos::is_view::value #ifdef KOKKOS_HAVE_CUDA && ! Impl::is_same< typename Impl::FunctorPolicyExecutionSpace< FunctorType , void >::execution_space, @@ -503,7 +527,11 @@ void parallel_reduce( const size_t work_count } #endif - (void) Impl::ParallelReduce< FunctorType, ExecPolicy >( Impl::CopyWithoutTracking::apply(functor) , ExecPolicy(0,work_count) , Impl::CopyWithoutTracking::apply(result_view) ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType, ExecPolicy > closure( functor , ExecPolicy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -564,7 +592,11 @@ void parallel_reduce( const size_t work_count } #endif - (void) Impl::ParallelReduce< FunctorType , policy >( Impl::CopyWithoutTracking::apply(functor) , policy(0,work_count) , Impl::CopyWithoutTracking::apply(result_view) ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelReduce< FunctorType , policy > closure( functor , policy(0,work_count) , result_view ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -813,7 +845,11 @@ void parallel_scan( const ExecutionPolicy & policy } #endif - Impl::ParallelScan< FunctorType , ExecutionPolicy > scan( Impl::CopyWithoutTracking::apply(functor) , policy ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelScan< FunctorType , ExecutionPolicy > closure( functor , policy ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { @@ -842,7 +878,11 @@ void parallel_scan( const size_t work_count } #endif - (void) Impl::ParallelScan< FunctorType , policy >( Impl::CopyWithoutTracking::apply(functor) , policy(0,work_count) ); + Kokkos::Impl::shared_allocation_tracking_claim_and_disable(); + Impl::ParallelScan< FunctorType , policy > closure( functor , policy(0,work_count) ); + Kokkos::Impl::shared_allocation_tracking_release_and_enable(); + + closure.execute(); #ifdef KOKKOSP_ENABLE_PROFILING if(Kokkos::Experimental::profileLibraryLoaded()) { diff --git a/lib/kokkos/core/src/Kokkos_Serial.hpp b/lib/kokkos/core/src/Kokkos_Serial.hpp index 5773a18b3f..8be973d442 100644 --- a/lib/kokkos/core/src/Kokkos_Serial.hpp +++ b/lib/kokkos/core/src/Kokkos_Serial.hpp @@ -151,7 +151,7 @@ public: static void finalize() {} //! Print configuration information to the given output stream. - static void print_configuration( std::ostream & , const bool detail = false ) {} + static void print_configuration( std::ostream & , const bool /* detail */ = false ) {} //-------------------------------------------------------------------------- @@ -295,6 +295,7 @@ class TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > private: const int m_league_size ; + const int m_scratch_size ; public: @@ -326,15 +327,55 @@ public: inline int team_size() const { return 1 ; } inline int league_size() const { return m_league_size ; } + inline size_t scratch_size() const { return m_scratch_size ; } /** \brief Specify league size, request team size */ - TeamPolicy( execution_space & , int league_size_request , int /* team_size_request */ , int vector_length_request = 1 ) + TeamPolicy( execution_space & + , int league_size_request + , int /* team_size_request */ + , int /* vector_length_request */ = 1 ) : m_league_size( league_size_request ) - { (void) vector_length_request; } + , m_scratch_size ( 0 ) + {} - TeamPolicy( int league_size_request , int /* team_size_request */ , int vector_length_request = 1 ) + TeamPolicy( execution_space & + , int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , int /* vector_length_request */ = 1 ) : m_league_size( league_size_request ) - { (void) vector_length_request; } + , m_scratch_size ( 0 ) + {} + + TeamPolicy( int league_size_request + , int /* team_size_request */ + , int /* vector_length_request */ = 1 ) + : m_league_size( league_size_request ) + , m_scratch_size ( 0 ) + {} + + TeamPolicy( int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , int /* vector_length_request */ = 1 ) + : m_league_size( league_size_request ) + , m_scratch_size ( 0 ) + {} + + template + TeamPolicy( int league_size_request + , int /* team_size_request */ + , const Experimental::TeamScratchRequest & scratch_request ) + : m_league_size(league_size_request) + , m_scratch_size(scratch_request.total(1)) + {} + + + template + TeamPolicy( int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , const Experimental::TeamScratchRequest & scratch_request ) + : m_league_size(league_size_request) + , m_scratch_size(scratch_request.total(1)) + {} typedef Impl::SerialTeamMember member_type ; }; @@ -346,53 +387,69 @@ public: /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ +/* Parallel patterns for Kokkos::Serial with RangePolicy */ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelFor< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > > +class ParallelFor< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > Policy ; -public: - // work tag is void - template< class PType > - inline - ParallelFor( typename Impl::enable_if< - ( Impl::is_same< PType , Policy >::value && - Impl::is_same< typename PType::work_tag , void >::value - ), const FunctorType & >::type functor - , const PType & policy ) + const FunctorType m_functor ; + const Policy m_policy ; + + template< class TagType > + KOKKOS_INLINE_FUNCTION + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec() const { - const typename PType::member_type e = policy.end(); - for ( typename PType::member_type i = policy.begin() ; i < e ; ++i ) { - functor( i ); + const typename Policy::member_type e = m_policy.end(); + for ( typename Policy::member_type i = m_policy.begin() ; i < e ; ++i ) { + m_functor( i ); } } - // work tag is non-void - template< class PType > - inline - ParallelFor( typename Impl::enable_if< - ( Impl::is_same< PType , Policy >::value && - ! Impl::is_same< typename PType::work_tag , void >::value - ), const FunctorType & >::type functor - , const PType & policy ) + template< class TagType > + KOKKOS_INLINE_FUNCTION + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec() const { - const typename PType::member_type e = policy.end(); - for ( typename PType::member_type i = policy.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i ); + const TagType t{} ; + const typename Policy::member_type e = m_policy.end(); + for ( typename Policy::member_type i = m_policy.begin() ; i < e ; ++i ) { + m_functor( t , i ); } } + +public: + + inline + void execute() const + { this-> template exec< typename Policy::work_tag >(); } + + inline + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + {} }; +/*--------------------------------------------------------------------------*/ + template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelReduce< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > > +class ParallelReduce< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > + > { -public: +private: + typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > Policy ; typedef typename Policy::work_tag WorkTag ; typedef Kokkos::Impl::FunctorValueTraits< FunctorType , WorkTag > ValueTraits ; @@ -401,123 +458,136 @@ public: typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - // Work tag is void - template< class ViewType , class PType > - ParallelReduce( typename Impl::enable_if< - ( Impl::is_view< ViewType >::value && - Impl::is_same< typename ViewType::memory_space , HostSpace >::value && - Impl::is_same< PType , Policy >::value && - Impl::is_same< typename PType::work_tag , void >::value - ), const FunctorType & >::type functor - , const PType & policy - , const ViewType & result - ) + const FunctorType m_functor ; + const Policy m_policy ; + const pointer_type m_result_ptr ; + + + template< class TagType > + inline + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec( pointer_type ptr ) const { - pointer_type result_ptr = result.ptr_on_device(); + reference_type update = ValueInit::init( m_functor , ptr ); - if ( ! result_ptr ) { - result_ptr = (pointer_type) - Kokkos::Serial::scratch_memory_resize( ValueTraits::value_size( functor ) , 0 ); + const typename Policy::member_type e = m_policy.end(); + for ( typename Policy::member_type i = m_policy.begin() ; i < e ; ++i ) { + m_functor( i , update ); } - reference_type update = ValueInit::init( functor , result_ptr ); - - const typename PType::member_type e = policy.end(); - for ( typename PType::member_type i = policy.begin() ; i < e ; ++i ) { - functor( i , update ); - } - - Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( functor , result_ptr ); + Kokkos::Impl::FunctorFinal< FunctorType , TagType >:: + final( m_functor , ptr ); } - // Work tag is non-void - template< class ViewType , class PType > - ParallelReduce( typename Impl::enable_if< - ( Impl::is_view< ViewType >::value && - Impl::is_same< typename ViewType::memory_space , HostSpace >::value && - Impl::is_same< PType , Policy >::value && - ! Impl::is_same< typename PType::work_tag , void >::value - ), const FunctorType & >::type functor - , const PType & policy - , const ViewType & result - ) + template< class TagType > + inline + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec( pointer_type ptr ) const { - pointer_type result_ptr = result.ptr_on_device(); + const TagType t{} ; + reference_type update = ValueInit::init( m_functor , ptr ); - if ( ! result_ptr ) { - result_ptr = (pointer_type) - Kokkos::Serial::scratch_memory_resize( ValueTraits::value_size( functor ) , 0 ); + const typename Policy::member_type e = m_policy.end(); + for ( typename Policy::member_type i = m_policy.begin() ; i < e ; ++i ) { + m_functor( t , i , update ); } - typename ValueTraits::reference_type update = ValueInit::init( functor , result_ptr ); + Kokkos::Impl::FunctorFinal< FunctorType , TagType >:: + final( m_functor , ptr ); + } - const typename PType::member_type e = policy.end(); - for ( typename PType::member_type i = policy.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i , update ); - } +public: - Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( functor , result_ptr ); + inline + void execute() const + { + pointer_type ptr = (pointer_type) Kokkos::Serial::scratch_memory_resize + ( ValueTraits::value_size( m_functor ) , 0 ); + + this-> template exec< WorkTag >( m_result_ptr ? m_result_ptr : ptr ); + } + + template< class ViewType > + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const ViewType & arg_result ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result.ptr_on_device() ) + { + static_assert( Kokkos::is_view< ViewType >::value + , "Reduction result on Kokkos::Serial must be a Kokkos::View" ); + + static_assert( std::is_same< typename ViewType::memory_space + , Kokkos::HostSpace >::value + , "Reduction result on Kokkos::Serial must be a Kokkos::View in HostSpace" ); } }; +/*--------------------------------------------------------------------------*/ + template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelScan< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > > +class ParallelScan< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Serial > Policy ; - - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , typename Policy::work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , typename Policy::work_tag > ValueInit ; - -public: + typedef typename Policy::work_tag WorkTag ; + typedef Kokkos::Impl::FunctorValueTraits< FunctorType , WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType , WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - // work tag is void - template< class PType > + const FunctorType m_functor ; + const Policy m_policy ; + + template< class TagType > inline - ParallelScan( typename Impl::enable_if< - ( Impl::is_same< PType , Policy >::value && - Impl::is_same< typename PType::work_tag , void >::value - ), const FunctorType & >::type functor - , const PType & policy ) + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec( pointer_type ptr ) const { - pointer_type result_ptr = (pointer_type) - Kokkos::Serial::scratch_memory_resize( ValueTraits::value_size( functor ) , 0 ); + reference_type update = ValueInit::init( m_functor , ptr ); - reference_type update = ValueInit::init( functor , result_ptr ); - - const typename PType::member_type e = policy.end(); - for ( typename PType::member_type i = policy.begin() ; i < e ; ++i ) { - functor( i , update , true ); + const typename Policy::member_type e = m_policy.end(); + for ( typename Policy::member_type i = m_policy.begin() ; i < e ; ++i ) { + m_functor( i , update , true ); } - - Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( functor , result_ptr ); } - // work tag is non-void - template< class PType > + template< class TagType > inline - ParallelScan( typename Impl::enable_if< - ( Impl::is_same< PType , Policy >::value && - ! Impl::is_same< typename PType::work_tag , void >::value - ), const FunctorType & >::type functor - , const PType & policy ) + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec( pointer_type ptr ) const { - pointer_type result_ptr = (pointer_type) - Kokkos::Serial::scratch_memory_resize( ValueTraits::value_size( functor ) , 0 ); + const TagType t{} ; + reference_type update = ValueInit::init( m_functor , ptr ); - reference_type update = ValueInit::init( functor , result_ptr ); - - const typename PType::member_type e = policy.end(); - for ( typename PType::member_type i = policy.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i , update , true ); + const typename Policy::member_type e = m_policy.end(); + for ( typename Policy::member_type i = m_policy.begin() ; i < e ; ++i ) { + m_functor( t , i , update , true ); } - - Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( functor , result_ptr ); } + +public: + + inline + void execute() const + { + pointer_type ptr = (pointer_type) + Kokkos::Serial::scratch_memory_resize( ValueTraits::value_size( m_functor ) , 0 ); + this-> template exec< WorkTag >( ptr ); + } + + inline + ParallelScan( const FunctorType & arg_functor + , const Policy & arg_policy + ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + {} }; } // namespace Impl @@ -525,112 +595,157 @@ public: /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ +/* Parallel patterns for Kokkos::Serial with TeamPolicy */ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 > -class ParallelFor< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > > +class ParallelFor< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > + > { private: typedef Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > Policy ; + typedef typename Policy::member_type Member ; + + const FunctorType m_functor ; + const int m_league ; + const int m_shared ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const FunctorType & >::type functor - , const typename Policy::member_type & member ) - { functor( member ); } - - template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const FunctorType & >::type functor - , const typename Policy::member_type & member ) - { functor( TagType() , member ); } - -public: - - ParallelFor( const FunctorType & functor - , const Policy & policy ) + inline + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec() const { - const int shared_size = FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ); - - Kokkos::Serial::scratch_memory_resize( 0 , shared_size ); - - for ( int ileague = 0 ; ileague < policy.league_size() ; ++ileague ) { - ParallelFor::template driver< typename Policy::work_tag > - ( functor , typename Policy::member_type(ileague,policy.league_size(),shared_size) ); - // functor( typename Policy::member_type(ileague,policy.league_size(),shared_size) ); + for ( int ileague = 0 ; ileague < m_league ; ++ileague ) { + m_functor( Member(ileague,m_league,m_shared) ); } } + + template< class TagType > + inline + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec() const + { + const TagType t{} ; + for ( int ileague = 0 ; ileague < m_league ; ++ileague ) { + m_functor( t , Member(ileague,m_league,m_shared) ); + } + } + +public: + + inline + void execute() const + { + Kokkos::Serial::scratch_memory_resize( 0 , m_shared ); + this-> template exec< typename Policy::work_tag >(); + } + + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_league( arg_policy.league_size() ) + , m_shared( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( arg_functor , 1 ) ) + { } }; +/*--------------------------------------------------------------------------*/ + template< class FunctorType , class Arg0 , class Arg1 > -class ParallelReduce< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > > +class ParallelReduce< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > + > { private: typedef Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Serial > Policy ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , typename Policy::work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , typename Policy::work_tag > ValueInit ; - -public: + typedef typename Policy::member_type Member ; + typedef typename Policy::work_tag WorkTag ; + typedef Kokkos::Impl::FunctorValueTraits< FunctorType , WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType , WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; -private: + const FunctorType m_functor ; + const int m_league ; + const int m_shared ; + pointer_type m_result_ptr ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const FunctorType & >::type functor - , const typename Policy::member_type & member - , reference_type update ) - { functor( member , update ); } + inline + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec( pointer_type ptr ) const + { + reference_type update = ValueInit::init( m_functor , ptr ); + + for ( int ileague = 0 ; ileague < m_league ; ++ileague ) { + m_functor( Member(ileague,m_league,m_shared) , update ); + } + + Kokkos::Impl::FunctorFinal< FunctorType , TagType >:: + final( m_functor , ptr ); + } template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const FunctorType & >::type functor - , const typename Policy::member_type & member - , reference_type update ) - { functor( TagType() , member , update ); } + inline + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec( pointer_type ptr ) const + { + const TagType t{} ; + + reference_type update = ValueInit::init( m_functor , ptr ); + + for ( int ileague = 0 ; ileague < m_league ; ++ileague ) { + m_functor( t , Member(ileague,m_league,m_shared) , update ); + } + + Kokkos::Impl::FunctorFinal< FunctorType , TagType >:: + final( m_functor , ptr ); + } public: - template< class ViewType > - ParallelReduce( const FunctorType & functor - , const Policy & policy - , const ViewType & result - ) + inline + void execute() const { - const int reduce_size = ValueTraits::value_size( functor ); - const int shared_size = FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ); - void * const scratch_reduce = Kokkos::Serial::scratch_memory_resize( reduce_size , shared_size ); + pointer_type ptr = (pointer_type) Kokkos::Serial::scratch_memory_resize + ( ValueTraits::value_size( m_functor ) , m_shared ); - const pointer_type result_ptr = - result.ptr_on_device() ? result.ptr_on_device() - : (pointer_type) scratch_reduce ; - - reference_type update = ValueInit::init( functor , result_ptr ); - - for ( int ileague = 0 ; ileague < policy.league_size() ; ++ileague ) { - ParallelReduce::template driver< typename Policy::work_tag > - ( functor , typename Policy::member_type(ileague,policy.league_size(),shared_size) , update ); - } - - Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( functor , result_ptr ); + this-> template exec< WorkTag >( m_result_ptr ? m_result_ptr : ptr ); } + + template< class ViewType > + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const ViewType & arg_result + ) + : m_functor( arg_functor ) + , m_league( arg_policy.league_size() ) + , m_shared( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( m_functor , 1 ) ) + , m_result_ptr( arg_result.ptr_on_device() ) + { + static_assert( Kokkos::is_view< ViewType >::value + , "Reduction result on Kokkos::Serial must be a Kokkos::View" ); + + static_assert( std::is_same< typename ViewType::memory_space + , Kokkos::HostSpace >::value + , "Reduction result on Kokkos::Serial must be a Kokkos::View in HostSpace" ); + } + }; } // namespace Impl } // namespace Kokkos -namespace Kokkos { +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/* Nested parallel patterns for Kokkos::Serial with TeamPolicy */ +namespace Kokkos { namespace Impl { template @@ -739,8 +854,6 @@ void parallel_reduce(const Impl::TeamThreadRangeBoundariesStruct()); } -#ifdef KOKKOS_HAVE_CXX11 - /** \brief Intra-thread vector parallel_reduce. Executes lambda(iType i, ValueType & val) for each i=0..N-1. * * The range i=0..N-1 is mapped to all vector lanes of the the calling thread and a reduction of @@ -764,8 +877,6 @@ void parallel_reduce(const Impl::TeamThreadRangeBoundariesStruct(join)); } -#endif // KOKKOS_HAVE_CXX11 - } //namespace Kokkos namespace Kokkos { diff --git a/lib/kokkos/core/src/Kokkos_View.hpp b/lib/kokkos/core/src/Kokkos_View.hpp index 531218b5d0..2f93f35412 100644 --- a/lib/kokkos/core/src/Kokkos_View.hpp +++ b/lib/kokkos/core/src/Kokkos_View.hpp @@ -47,11 +47,12 @@ #include #include #include -#include -#include #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) +#include +#include + #include #include #include @@ -444,14 +445,14 @@ template< class DataType , typename ViewTraits::specialize > class View ; -namespace Impl { - template< class C > -struct is_view : public bool_< false > {}; +struct is_view : public Impl::bool_< false > {}; template< class D , class A1 , class A2 , class A3 , class S > -struct is_view< View< D , A1 , A2 , A3 , S > > : public bool_< true > {}; +struct is_view< View< D , A1 , A2 , A3 , S > > : public Impl::bool_< true > {}; +namespace Impl { +using Kokkos::is_view ; } //---------------------------------------------------------------------------- @@ -952,33 +953,37 @@ public: Impl::ViewError::scalar_operator_called_from_non_scalar_view > if_scalar_operator ; + typedef Impl::if_c< traits::rank == 0 , + reference_type , + Impl::ViewError::scalar_operator_called_from_non_scalar_view > + if_scalar_operator_return ; KOKKOS_INLINE_FUNCTION const View & operator = ( const typename if_scalar_operator::type & rhs ) const { KOKKOS_RESTRICT_EXECUTION_TO_DATA( typename traits::memory_space , ptr_on_device() ); - *m_ptr_on_device = if_scalar_operator::select( rhs ); + m_ptr_on_device[ 0 ] = if_scalar_operator::select( rhs ); return *this ; } KOKKOS_FORCEINLINE_FUNCTION - operator typename if_scalar_operator::type & () const + operator typename if_scalar_operator_return::type () const { KOKKOS_RESTRICT_EXECUTION_TO_DATA( typename traits::memory_space , ptr_on_device() ); - return if_scalar_operator::select( *m_ptr_on_device ); + return if_scalar_operator_return::select( m_ptr_on_device[ 0 ] ); } KOKKOS_FORCEINLINE_FUNCTION - typename if_scalar_operator::type & operator()() const + typename if_scalar_operator_return::type operator()() const { KOKKOS_RESTRICT_EXECUTION_TO_DATA( typename traits::memory_space , ptr_on_device() ); - return if_scalar_operator::select( *m_ptr_on_device ); + return if_scalar_operator_return::select( m_ptr_on_device[ 0 ] ); } KOKKOS_FORCEINLINE_FUNCTION - typename if_scalar_operator::type & operator*() const + typename if_scalar_operator_return::type operator*() const { KOKKOS_RESTRICT_EXECUTION_TO_DATA( typename traits::memory_space , ptr_on_device() ); - return if_scalar_operator::select( *m_ptr_on_device ); + return if_scalar_operator_return::select( m_ptr_on_device[ 0 ] ); } //------------------------------------ @@ -1849,6 +1854,8 @@ void resize( View & v , Impl::ViewRemap< view_type , view_type >( v_resized , v ); + view_type::execution_space::fence(); + v = v_resized ; } @@ -2092,27 +2099,10 @@ struct ALL { KOKKOS_INLINE_FUNCTION ALL(){} }; //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -#include - -#else - -// Must define before includng -namespace Kokkos { -namespace Experimental { -namespace Impl { -struct ALL_t ; -} -} -using ALL = Experimental::Impl::ALL_t ; -} - -#include -#include +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ #include -#endif /* #if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ - //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- diff --git a/lib/kokkos/core/src/Kokkos_hwloc.hpp b/lib/kokkos/core/src/Kokkos_hwloc.hpp index a0b007f642..ff713c9523 100644 --- a/lib/kokkos/core/src/Kokkos_hwloc.hpp +++ b/lib/kokkos/core/src/Kokkos_hwloc.hpp @@ -1,13 +1,13 @@ /* //@HEADER // ************************************************************************ -// +// // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation -// +// // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,7 +36,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) -// +// // ************************************************************************ //@HEADER */ @@ -109,7 +109,7 @@ unsigned thread_mapping( const char * const label , /** \brief Query core-coordinate of the current thread * with respect to the core_topology. * - * As long as the thread is running within the + * As long as the thread is running within the * process binding the following condition holds. * * core_coordinate.first < core_topology.first @@ -120,6 +120,10 @@ std::pair get_this_thread_coordinate(); /** \brief Bind the current thread to a core. */ bool bind_this_thread( const std::pair ); + +/** \brief Can hwloc bind threads? */ +bool can_bind_threads(); + /** \brief Bind the current thread to one of the cores in the list. * Set that entry to (~0,~0) and return the index. * If binding fails return ~0. diff --git a/lib/kokkos/core/src/Makefile b/lib/kokkos/core/src/Makefile index 8bb3508592..e7dc1ebeec 100644 --- a/lib/kokkos/core/src/Makefile +++ b/lib/kokkos/core/src/Makefile @@ -4,14 +4,14 @@ PREFIX ?= /usr/local/lib/kokkos default: messages build-lib echo "End Build" - + include $(KOKKOS_PATH)/Makefile.kokkos ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) - CXX = nvcc_wrapper + CXX = $(NVCC_WRAPPER) CXXFLAGS ?= -O3 - LINK = nvcc_wrapper + LINK = $(NVCC_WRAPPER) LINKFLAGS ?= else CXX ?= g++ @@ -62,8 +62,10 @@ build-makefile-kokkos: echo "KOKKOS_DEBUG = $(KOKKOS_DEBUG)" >> Makefile.kokkos echo "KOKKOS_USE_TPLS = $(KOKKOS_USE_TPLS)" >> Makefile.kokkos echo "KOKKOS_CXX_STANDARD = $(KOKKOS_CXX_STANDARD)" >> Makefile.kokkos + echo "KOKKOS_OPTIONS = $(KOKKOS_OPTIONS)" >> Makefile.kokkos echo "KOKKOS_CUDA_OPTIONS = $(KOKKOS_CUDA_OPTIONS)" >> Makefile.kokkos echo "CXX ?= $(CXX)" >> Makefile.kokkos + echo "NVCC_WRAPPER ?= $(PREFIX)/bin/nvcc_wrapper" >> Makefile.kokkos echo "" >> Makefile.kokkos echo "#Source and Header files of Kokkos relative to KOKKOS_PATH" >> Makefile.kokkos echo "KOKKOS_HEADERS = $(KOKKOS_HEADERS)" >> Makefile.kokkos @@ -90,6 +92,7 @@ build-lib: build-makefile-kokkos $(KOKKOS_LINK_DEPENDS) mkdir: mkdir -p $(PREFIX) + mkdir -p $(PREFIX)/bin mkdir -p $(PREFIX)/include mkdir -p $(PREFIX)/lib mkdir -p $(PREFIX)/include/impl @@ -97,7 +100,7 @@ mkdir: copy-cuda: mkdir mkdir -p $(PREFIX)/include/Cuda cp $(KOKKOS_HEADERS_CUDA) $(PREFIX)/include/Cuda - + copy-threads: mkdir mkdir -p $(PREFIX)/include/Threads cp $(KOKKOS_HEADERS_THREADS) $(PREFIX)/include/Threads @@ -111,13 +114,14 @@ copy-openmp: mkdir cp $(KOKKOS_HEADERS_OPENMP) $(PREFIX)/include/OpenMP install: mkdir $(CONDITIONAL_COPIES) build-lib + cp $(NVCC_WRAPPER) $(PREFIX)/bin cp $(KOKKOS_HEADERS_INCLUDE) $(PREFIX)/include cp $(KOKKOS_HEADERS_INCLUDE_IMPL) $(PREFIX)/include/impl cp Makefile.kokkos $(PREFIX) cp libkokkos.a $(PREFIX)/lib cp KokkosCore_config.h $(PREFIX)/include - + clean: kokkos-clean rm Makefile.kokkos diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel.hpp index f8393611e4..f1a8397e95 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel.hpp @@ -57,41 +57,57 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelFor< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > > +class ParallelFor< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > Policy ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::member_type Member ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , const PType & range ) + const FunctorType m_functor ; + const Policy m_policy ; + + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend ) { - const typename PType::member_type work_end = range.end(); - for ( typename PType::member_type iwork = range.begin() ; iwork < work_end ; ++iwork ) { + #ifdef KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION + #ifdef KOKKOS_HAVE_PRAGMA_IVDEP + #pragma ivdep + #endif + #endif + for ( Member iwork = ibeg ; iwork < iend ; ++iwork ) { functor( iwork ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend ) { - const typename PType::member_type work_end = range.end(); - for ( typename PType::member_type iwork = range.begin() ; iwork < work_end ; ++iwork ) { - functor( typename PType::work_tag() , iwork ); + const TagType t{} ; + #ifdef KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION + #ifdef KOKKOS_HAVE_PRAGMA_IVDEP + #pragma ivdep + #endif + #endif + for ( Member iwork = ibeg ; iwork < iend ; ++iwork ) { + functor( t , iwork ); } } public: inline - ParallelFor( const FunctorType & functor - , const Policy & policy ) + void execute() const { OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_for"); OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_for"); @@ -99,10 +115,20 @@ public: #pragma omp parallel { OpenMPexec & exec = * OpenMPexec::get_thread_omp(); - driver( functor , typename Policy::WorkRange( policy , exec.pool_rank() , exec.pool_size() ) ); + + const WorkRange range( m_policy, exec.pool_rank(), exec.pool_size() ); + + ParallelFor::template exec_range< WorkTag >( m_functor , range.begin() , range.end() ); } /* END #pragma omp parallel */ } + + inline + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + {} }; } // namespace Impl @@ -115,90 +141,119 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelReduce< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > > +class ParallelReduce< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > Policy ; - typedef typename Policy::work_tag WorkTag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , WorkTag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , WorkTag > ValueInit ; - typedef Kokkos::Impl::FunctorValueJoin< FunctorType , WorkTag > ValueJoin ; + + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::member_type Member ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; + typedef Kokkos::Impl::FunctorValueJoin< FunctorType, WorkTag > ValueJoin ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , reference_type update - , const PType & range ) + const FunctorType m_functor ; + const Policy m_policy ; + const pointer_type m_result_ptr ; + + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update ) { - const typename PType::member_type work_end = range.end(); - for ( typename PType::member_type iwork = range.begin() ; iwork < work_end ; ++iwork ) { + #ifdef KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION + #ifdef KOKKOS_HAVE_PRAGMA_IVDEP + #pragma ivdep + #endif + #endif + for ( Member iwork = ibeg ; iwork < iend ; ++iwork ) { functor( iwork , update ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , reference_type update - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update ) { - const typename PType::member_type work_end = range.end(); - for ( typename PType::member_type iwork = range.begin() ; iwork < work_end ; ++iwork ) { - functor( typename PType::work_tag() , iwork , update ); + const TagType t{} ; + #ifdef KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION + #ifdef KOKKOS_HAVE_PRAGMA_IVDEP + #pragma ivdep + #endif + #endif + for ( Member iwork = ibeg ; iwork < iend ; ++iwork ) { + functor( t , iwork , update ); } } public: + inline + void execute() const + { + OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_reduce"); + OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_reduce"); + + OpenMPexec::resize_scratch( ValueTraits::value_size( m_functor ) , 0 ); + +#pragma omp parallel + { + OpenMPexec & exec = * OpenMPexec::get_thread_omp(); + const WorkRange range( m_policy, exec.pool_rank(), exec.pool_size() ); + ParallelReduce::template exec_range< WorkTag > + ( m_functor , range.begin() , range.end() + , ValueInit::init( m_functor , exec.scratch_reduce() ) ); + } +/* END #pragma omp parallel */ + + // Reduction: + + const pointer_type ptr = pointer_type( OpenMPexec::pool_rev(0)->scratch_reduce() ); + + for ( int i = 1 ; i < OpenMPexec::pool_size() ; ++i ) { + ValueJoin::join( m_functor , ptr , OpenMPexec::pool_rev(i)->scratch_reduce() ); + } + + Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( m_functor , ptr ); + + if ( m_result_ptr ) { + const int n = ValueTraits::value_count( m_functor ); + + for ( int j = 0 ; j < n ; ++j ) { m_result_ptr[j] = ptr[j] ; } + } + } + //---------------------------------------- template< class ViewType > inline - ParallelReduce( typename Impl::enable_if< - ( Impl::is_view< ViewType >::value && - Impl::is_same< typename ViewType::memory_space , HostSpace >::value - ), const FunctorType & >::type functor - , const Policy & policy - , const ViewType & result_view ) - { - OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_reduce"); - OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_reduce"); - - OpenMPexec::resize_scratch( ValueTraits::value_size( functor ) , 0 ); - -#pragma omp parallel + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const ViewType & arg_result_view ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result_view.ptr_on_device() ) { - OpenMPexec & exec = * OpenMPexec::get_thread_omp(); + static_assert( Kokkos::is_view< ViewType >::value + , "Reduction result on Kokkos::OpenMP must be a Kokkos::View" ); - driver( functor - , ValueInit::init( functor , exec.scratch_reduce() ) - , typename Policy::WorkRange( policy , exec.pool_rank() , exec.pool_size() ) - ); + static_assert( std::is_same< typename ViewType::memory_space + , Kokkos::HostSpace >::value + , "Reduction result on Kokkos::OpenMP must be a Kokkos::View in HostSpace" ); } -/* END #pragma omp parallel */ - - { - const pointer_type ptr = pointer_type( OpenMPexec::pool_rev(0)->scratch_reduce() ); - - for ( int i = 1 ; i < OpenMPexec::pool_size() ; ++i ) { - ValueJoin::join( functor , ptr , OpenMPexec::pool_rev(i)->scratch_reduce() ); - } - - Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( functor , ptr ); - - if ( result_view.ptr_on_device() ) { - const int n = ValueTraits::value_count( functor ); - - for ( int j = 0 ; j < n ; ++j ) { result_view.ptr_on_device()[j] = ptr[j] ; } - } - } - } }; } // namespace Impl @@ -211,106 +266,129 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelScan< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > > +class ParallelScan< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::OpenMP > Policy ; - typedef typename Policy::work_tag WorkTag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , WorkTag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , WorkTag > ValueInit ; - typedef Kokkos::Impl::FunctorValueJoin< FunctorType , WorkTag > ValueJoin ; - typedef Kokkos::Impl::FunctorValueOps< FunctorType , WorkTag > ValueOps ; + + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::member_type Member ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; + typedef Kokkos::Impl::FunctorValueJoin< FunctorType, WorkTag > ValueJoin ; + typedef Kokkos::Impl::FunctorValueOps< FunctorType, WorkTag > ValueOps ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , reference_type update - , const PType & range - , const bool final ) + const FunctorType m_functor ; + const Policy m_policy ; + + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update , const bool final ) { - const typename PType::member_type work_end = range.end(); - for ( typename PType::member_type iwork = range.begin() ; iwork < work_end ; ++iwork ) { + #ifdef KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION + #ifdef KOKKOS_HAVE_PRAGMA_IVDEP + #pragma ivdep + #endif + #endif + for ( Member iwork = ibeg ; iwork < iend ; ++iwork ) { functor( iwork , update , final ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , reference_type update - , const PType & range - , const bool final ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update , const bool final ) { - const typename PType::member_type work_end = range.end(); - for ( typename PType::member_type iwork = range.begin() ; iwork < work_end ; ++iwork ) { - functor( typename PType::work_tag() , iwork , update , final ); + const TagType t{} ; + #ifdef KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION + #ifdef KOKKOS_HAVE_PRAGMA_IVDEP + #pragma ivdep + #endif + #endif + for ( Member iwork = ibeg ; iwork < iend ; ++iwork ) { + functor( t , iwork , update , final ); } } public: + inline + void execute() const + { + OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_scan"); + OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_scan"); + + OpenMPexec::resize_scratch( 2 * ValueTraits::value_size( m_functor ) , 0 ); + +#pragma omp parallel + { + OpenMPexec & exec = * OpenMPexec::get_thread_omp(); + const WorkRange range( m_policy, exec.pool_rank(), exec.pool_size() ); + const pointer_type ptr = + pointer_type( exec.scratch_reduce() ) + + ValueTraits::value_count( m_functor ); + ParallelScan::template exec_range< WorkTag > + ( m_functor , range.begin() , range.end() + , ValueInit::init( m_functor , ptr ) , false ); + } +/* END #pragma omp parallel */ + + { + const unsigned thread_count = OpenMPexec::pool_size(); + const unsigned value_count = ValueTraits::value_count( m_functor ); + + pointer_type ptr_prev = 0 ; + + for ( unsigned rank_rev = thread_count ; rank_rev-- ; ) { + + pointer_type ptr = pointer_type( OpenMPexec::pool_rev(rank_rev)->scratch_reduce() ); + + if ( ptr_prev ) { + for ( unsigned i = 0 ; i < value_count ; ++i ) { ptr[i] = ptr_prev[ i + value_count ] ; } + ValueJoin::join( m_functor , ptr + value_count , ptr ); + } + else { + ValueInit::init( m_functor , ptr ); + } + + ptr_prev = ptr ; + } + } + +#pragma omp parallel + { + OpenMPexec & exec = * OpenMPexec::get_thread_omp(); + const WorkRange range( m_policy, exec.pool_rank(), exec.pool_size() ); + const pointer_type ptr = pointer_type( exec.scratch_reduce() ); + ParallelScan::template exec_range< WorkTag > + ( m_functor , range.begin() , range.end() + , ValueOps::reference( ptr ) , true ); + } +/* END #pragma omp parallel */ + } + //---------------------------------------- inline - ParallelScan( const FunctorType & functor - , const Policy & policy ) - { - OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_scan"); - OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_scan"); - - OpenMPexec::resize_scratch( 2 * ValueTraits::value_size( functor ) , 0 ); - -#pragma omp parallel - { - OpenMPexec & exec = * OpenMPexec::get_thread_omp(); - - driver( functor - , ValueInit::init( functor , pointer_type( exec.scratch_reduce() ) + ValueTraits::value_count( functor ) ) - , typename Policy::WorkRange( policy , exec.pool_rank() , exec.pool_size() ) - , false ); - } -/* END #pragma omp parallel */ - - { - const unsigned thread_count = OpenMPexec::pool_size(); - const unsigned value_count = ValueTraits::value_count( functor ); - - pointer_type ptr_prev = 0 ; - - for ( unsigned rank_rev = thread_count ; rank_rev-- ; ) { - - pointer_type ptr = pointer_type( OpenMPexec::pool_rev(rank_rev)->scratch_reduce() ); - - if ( ptr_prev ) { - for ( unsigned i = 0 ; i < value_count ; ++i ) { ptr[i] = ptr_prev[ i + value_count ] ; } - ValueJoin::join( functor , ptr + value_count , ptr ); - } - else { - ValueInit::init( functor , ptr ); - } - - ptr_prev = ptr ; - } - } - -#pragma omp parallel - { - OpenMPexec & exec = * OpenMPexec::get_thread_omp(); - - driver( functor - , ValueOps::reference( pointer_type( exec.scratch_reduce() ) ) - , typename Policy::WorkRange( policy , exec.pool_rank() , exec.pool_size() ) - , true ); - } -/* END #pragma omp parallel */ - - } + ParallelScan( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + {} //---------------------------------------- }; @@ -325,62 +403,84 @@ namespace Kokkos { namespace Impl { template< class FunctorType , class Arg0 , class Arg1 > -class ParallelFor< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::OpenMP > > +class ParallelFor< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::OpenMP > + > { private: typedef Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::OpenMP > Policy ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + + const FunctorType m_functor ; + const Policy m_policy ; + const int m_shmem_size ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const FunctorType & >::type functor - , const typename Policy::member_type & member ) - { functor( member ); } + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member ) + { + for ( ; member.valid() ; member.next() ) { + functor( member ); + } + } template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const FunctorType & >::type functor - , const typename Policy::member_type & member ) - { functor( TagType() , member ); } + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member ) + { + const TagType t{} ; + for ( ; member.valid() ; member.next() ) { + functor( t , member ); + } + } public: inline - ParallelFor( const FunctorType & functor , - const Policy & policy ) - { - OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_for"); - OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_for"); + void execute() const + { + OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_for"); + OpenMPexec::verify_initialized("Kokkos::OpenMP parallel_for"); - const size_t team_reduce_size = Policy::member_type::team_reduce_size(); - const size_t team_shmem_size = FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ); + const size_t team_reduce_size = Policy::member_type::team_reduce_size(); - OpenMPexec::resize_scratch( 0 , team_reduce_size + team_shmem_size ); + OpenMPexec::resize_scratch( 0 , team_reduce_size + m_shmem_size ); #pragma omp parallel - { - typename Policy::member_type member( * OpenMPexec::get_thread_omp() , policy , team_shmem_size ); - - for ( ; member.valid() ; member.next() ) { - ParallelFor::template driver< typename Policy::work_tag >( functor , member ); + { + ParallelFor::template exec_team< WorkTag > + ( m_functor + , Member( * OpenMPexec::get_thread_omp(), m_policy, m_shmem_size) ); } - } /* END #pragma omp parallel */ - } + } - void wait() {} + inline + ParallelFor( const FunctorType & arg_functor , + const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_shmem_size( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( arg_functor , arg_policy.team_size() ) ) + {} }; template< class FunctorType , class Arg0 , class Arg1 > -class ParallelReduce< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::OpenMP > > +class ParallelReduce< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::OpenMP > + > { private: typedef Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::OpenMP > Policy ; - typedef typename Policy::work_tag WorkTag ; + + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + typedef Kokkos::Impl::FunctorValueTraits< FunctorType , WorkTag > ValueTraits ; typedef Kokkos::Impl::FunctorValueInit< FunctorType , WorkTag > ValueInit ; typedef Kokkos::Impl::FunctorValueJoin< FunctorType , WorkTag > ValueJoin ; @@ -388,102 +488,85 @@ private: typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; + const FunctorType m_functor ; + const Policy m_policy ; + const pointer_type m_result_ptr ; + const int m_shmem_size ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , const typename PType::member_type & member - , reference_type update ) - { functor( member , update ); } + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member , reference_type update ) + { + for ( ; member.valid() ; member.next() ) { + functor( member , update ); + } + } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< ! Impl::is_same< typename PType::work_tag , void >::value , - const FunctorType & >::type functor - , const typename PType::member_type & member - , reference_type update ) - { functor( typename PType::work_tag() , member , update ); } + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member , reference_type update ) + { + const TagType t{} ; + for ( ; member.valid() ; member.next() ) { + functor( t , member , update ); + } + } public: inline - ParallelReduce( const FunctorType & functor , - const Policy & policy ) - { - OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_reduce"); + void execute() const + { + OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_reduce"); - const size_t team_reduce_size = Policy::member_type::team_reduce_size(); - const size_t team_shmem_size = FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ); + const size_t team_reduce_size = Policy::member_type::team_reduce_size(); - OpenMPexec::resize_scratch( ValueTraits::value_size( functor ) , team_reduce_size + team_shmem_size ); + OpenMPexec::resize_scratch( ValueTraits::value_size( m_functor ) , team_reduce_size + m_shmem_size ); #pragma omp parallel - { - OpenMPexec & exec = * OpenMPexec::get_thread_omp(); + { + OpenMPexec & exec = * OpenMPexec::get_thread_omp(); - reference_type update = ValueInit::init( functor , exec.scratch_reduce() ); - - for ( typename Policy::member_type member( exec , policy , team_shmem_size ); member.valid() ; member.next() ) { - ParallelReduce::template driver< Policy >( functor , member , update ); + ParallelReduce::template exec_team< WorkTag > + ( m_functor + , Member( exec , m_policy , m_shmem_size ) + , ValueInit::init( m_functor , exec.scratch_reduce() ) ); } - } /* END #pragma omp parallel */ - { - typedef Kokkos::Impl::FunctorValueJoin< FunctorType , WorkTag , reference_type > Join ; + { + const pointer_type ptr = pointer_type( OpenMPexec::pool_rev(0)->scratch_reduce() ); - const pointer_type ptr = pointer_type( OpenMPexec::pool_rev(0)->scratch_reduce() ); + int max_active_threads = OpenMPexec::pool_size(); + if( max_active_threads > m_policy.league_size()* m_policy.team_size() ) + max_active_threads = m_policy.league_size()* m_policy.team_size(); - for ( int i = 1 ; i < OpenMPexec::pool_size() ; ++i ) { - Join::join( functor , ptr , OpenMPexec::pool_rev(i)->scratch_reduce() ); + for ( int i = 1 ; i < max_active_threads ; ++i ) { + ValueJoin::join( m_functor , ptr , OpenMPexec::pool_rev(i)->scratch_reduce() ); + } + + Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( m_functor , ptr ); + + if ( m_result_ptr ) { + const int n = ValueTraits::value_count( m_functor ); + + for ( int j = 0 ; j < n ; ++j ) { m_result_ptr[j] = ptr[j] ; } + } } - - Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( functor , ptr ); } - } template< class ViewType > inline - ParallelReduce( const FunctorType & functor , - const Policy & policy , - const ViewType & result ) - { - OpenMPexec::verify_is_process("Kokkos::OpenMP parallel_reduce"); - - const size_t team_reduce_size = Policy::member_type::team_reduce_size(); - const size_t team_shmem_size = FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ); - - OpenMPexec::resize_scratch( ValueTraits::value_size( functor ) , team_reduce_size + team_shmem_size ); - -#pragma omp parallel - { - OpenMPexec & exec = * OpenMPexec::get_thread_omp(); - - reference_type update = ValueInit::init( functor , exec.scratch_reduce() ); - - for ( typename Policy::member_type member( exec , policy , team_shmem_size ); member.valid() ; member.next() ) { - ParallelReduce::template driver< Policy >( functor , member , update ); - } - } -/* END #pragma omp parallel */ - - { - const pointer_type ptr = pointer_type( OpenMPexec::pool_rev(0)->scratch_reduce() ); - - for ( int i = 1 ; i < OpenMPexec::pool_size() ; ++i ) { - ValueJoin::join( functor , ptr , OpenMPexec::pool_rev(i)->scratch_reduce() ); - } - - Kokkos::Impl::FunctorFinal< FunctorType , WorkTag >::final( functor , ptr ); - - const int n = ValueTraits::value_count( functor ); - - for ( int j = 0 ; j < n ; ++j ) { result.ptr_on_device()[j] = ptr[j] ; } - } - } - - void wait() {} + ParallelReduce( const FunctorType & arg_functor , + const Policy & arg_policy , + const ViewType & arg_result ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result.ptr_on_device() ) + , m_shmem_size( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( arg_functor , arg_policy.team_size() ) ) + {} }; } // namespace Impl diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.cpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.cpp index ed98fd2f97..3e0fc42a68 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.cpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.cpp @@ -1,13 +1,13 @@ /* //@HEADER // ************************************************************************ -// +// // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation -// +// // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,7 +36,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) -// +// // ************************************************************************ //@HEADER */ @@ -84,8 +84,16 @@ int OpenMPexec::m_map_rank[ OpenMPexec::MAX_THREAD_COUNT ] = { 0 }; int OpenMPexec::m_pool_topo[ 4 ] = { 0 }; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + OpenMPexec::Pool OpenMPexec::m_pool; +#else + +OpenMPexec * OpenMPexec::m_pool[ OpenMPexec::MAX_THREAD_COUNT ] = { 0 }; + +#endif + void OpenMPexec::verify_is_process( const char * const label ) { if ( omp_in_parallel() ) { @@ -102,6 +110,13 @@ void OpenMPexec::verify_initialized( const char * const label ) msg.append( " ERROR: not initialized" ); Kokkos::Impl::throw_runtime_exception( msg ); } + + if ( omp_get_max_threads() != Kokkos::OpenMP::thread_pool_size(0) ) { + std::string msg( label ); + msg.append( " ERROR: Initialized but threads modified inappropriately" ); + Kokkos::Impl::throw_runtime_exception( msg ); + } + } void OpenMPexec::clear_scratch() @@ -109,7 +124,16 @@ void OpenMPexec::clear_scratch() #pragma omp parallel { const int rank_rev = m_map_rank[ omp_get_thread_num() ]; +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::HostSpace , void > Record ; + if ( m_pool[ rank_rev ] ) { + Record * const r = Record::get_record( m_pool[ rank_rev ] ); + m_pool[ rank_rev ] = 0 ; + Record::decrement( r ); + } +#else m_pool.at(rank_rev).clear(); +#endif } /* END #pragma omp parallel */ } @@ -147,7 +171,27 @@ void OpenMPexec::resize_scratch( size_t reduce_size , size_t thread_size ) const int rank_rev = m_map_rank[ omp_get_thread_num() ]; const int rank = pool_size - ( rank_rev + 1 ); - m_pool.at(rank_rev) = HostSpace::allocate_and_track( "openmp_scratch", alloc_size ); +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::HostSpace , void > Record ; + + Record * const r = Record::allocate( Kokkos::HostSpace() + , "openmp_scratch" + , alloc_size ); + + Record::increment( r ); + + m_pool[ rank_rev ] = reinterpret_cast( r->data() ); + +#else + + #pragma omp critical + { + m_pool.at(rank_rev) = HostSpace::allocate_and_track( "openmp_scratch", alloc_size ); + } + +#endif + new ( m_pool[ rank_rev ] ) OpenMPexec( rank , ALLOC_EXEC , reduce_size , thread_size ); } /* END #pragma omp parallel */ @@ -248,7 +292,9 @@ void OpenMP::initialize( unsigned thread_count , // Reverse the rank for threads so that the scan operation reduces to the highest rank thread. const unsigned omp_rank = omp_get_thread_num(); - const unsigned thread_r = Impl::s_using_hwloc ? Kokkos::hwloc::bind_this_thread( thread_count , threads_coord ) : omp_rank ; + const unsigned thread_r = Impl::s_using_hwloc && Kokkos::hwloc::can_bind_threads() + ? Kokkos::hwloc::bind_this_thread( thread_count , threads_coord ) + : omp_rank ; Impl::OpenMPexec::m_map_rank[ omp_rank ] = thread_r ; } @@ -293,7 +339,7 @@ void OpenMP::finalize() omp_set_num_threads(1); - if ( Impl::s_using_hwloc ) { + if ( Impl::s_using_hwloc && Kokkos::hwloc::can_bind_threads() ) { hwloc::unbind_this_thread(); } } diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.hpp index 1ab08f648d..d0086a2432 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMPexec.hpp @@ -61,6 +61,8 @@ public: enum { MAX_THREAD_COUNT = 4096 }; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + struct Pool { Pool() : m_trackers() {} @@ -78,11 +80,21 @@ public: } }; + private: + static Pool m_pool; // Indexed by: m_pool_rank_rev + +#else + +private: + + static OpenMPexec * m_pool[ MAX_THREAD_COUNT ]; // Indexed by: m_pool_rank_rev + +#endif + static int m_pool_topo[ 4 ]; static int m_map_rank[ MAX_THREAD_COUNT ]; - static Pool m_pool; // Indexed by: m_pool_rank_rev friend class Kokkos::OpenMP ; @@ -193,12 +205,14 @@ private: inline bool team_fan_in() const { + memory_fence(); for ( int n = 1 , j ; ( ( j = m_team_rank_rev + n ) < m_team_size ) && ! ( m_team_rank_rev & n ) ; n <<= 1 ) { m_exec.pool_rev( m_team_base_rev + j )->state_wait( Active ); } if ( m_team_rank_rev ) { m_exec.state_set( Rendezvous ); + memory_fence(); m_exec.state_wait( Rendezvous ); } @@ -208,8 +222,10 @@ private: inline void team_fan_out() const { + memory_fence(); for ( int n = 1 , j ; ( ( j = m_team_rank_rev + n ) < m_team_size ) && ! ( m_team_rank_rev & n ) ; n <<= 1 ) { m_exec.pool_rev( m_team_base_rev + j )->state_set( Active ); + memory_fence(); } } @@ -265,6 +281,7 @@ public: { return ValueType(); } #else { + memory_fence(); typedef ValueType value_type; const JoinLambdaAdapter op(op_in); #endif @@ -301,6 +318,7 @@ public: for ( int i = 1 ; i < m_team_size ; ++i ) { op.join( *team_value , *((type*) m_exec.pool_rev( m_team_base_rev + i )->scratch_thread()) ); } + memory_fence(); // The base team member may "lap" the other team members, // copy to their local value before proceeding. @@ -484,6 +502,8 @@ private: int m_team_alloc ; int m_team_iter ; + size_t m_scratch_size; + inline void init( const int league_size_request , const int team_size_request ) { @@ -511,13 +531,49 @@ public: inline int team_size() const { return m_team_size ; } inline int league_size() const { return m_league_size ; } + inline size_t scratch_size() const { return m_scratch_size ; } /** \brief Specify league size, request team size */ - TeamPolicy( execution_space & , int league_size_request , int team_size_request , int vector_length_request = 1) - { init( league_size_request , team_size_request ); (void) vector_length_request; } + TeamPolicy( execution_space & + , int league_size_request + , int team_size_request + , int /* vector_length_request */ = 1 ) + : m_scratch_size ( 0 ) + { init( league_size_request , team_size_request ); } - TeamPolicy( int league_size_request , int team_size_request , int vector_length_request = 1 ) - { init( league_size_request , team_size_request ); (void) vector_length_request; } + TeamPolicy( execution_space & + , int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , int /* vector_length_request */ = 1) + : m_scratch_size ( 0 ) + { init( league_size_request , execution_space::thread_pool_size(2) ); } + + TeamPolicy( int league_size_request + , int team_size_request + , int /* vector_length_request */ = 1 ) + : m_scratch_size ( 0 ) + { init( league_size_request , team_size_request ); } + + TeamPolicy( int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , int /* vector_length_request */ = 1 ) + : m_scratch_size ( 0 ) + { init( league_size_request , execution_space::thread_pool_size(2) ); } + + template + TeamPolicy( int league_size_request + , int team_size_request + , const Experimental::TeamScratchRequest & scratch_request ) + : m_scratch_size(scratch_request.total(team_size_request)) + { init(league_size_request,team_size_request); } + + + template + TeamPolicy( int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , const Experimental::TeamScratchRequest & scratch_request ) + : m_scratch_size(scratch_request.total(execution_space::thread_pool_size(2))) + { init(league_size_request,execution_space::thread_pool_size(2)); } inline int team_alloc() const { return m_team_alloc ; } inline int team_iter() const { return m_team_iter ; } diff --git a/lib/kokkos/core/src/Qthread/Kokkos_QthreadExec.hpp b/lib/kokkos/core/src/Qthread/Kokkos_QthreadExec.hpp index d772aee2bf..e3702167ef 100644 --- a/lib/kokkos/core/src/Qthread/Kokkos_QthreadExec.hpp +++ b/lib/kokkos/core/src/Qthread/Kokkos_QthreadExec.hpp @@ -212,7 +212,7 @@ public: // Join from lower ranking to higher ranking worker. // Value at m_worker_base[n-1] is zero so skip adding it to m_worker_base[n-2]. - for ( int i = m_worker_size - 1 ; --i ; ) { + for ( int i = m_worker_size - 1 ; --i > 0 ; ) { ValueJoin::join( func , m_worker_base[i-1]->m_scratch_alloc , m_worker_base[i]->m_scratch_alloc ); } } diff --git a/lib/kokkos/core/src/Qthread/Kokkos_Qthread_Parallel.hpp b/lib/kokkos/core/src/Qthread/Kokkos_Qthread_Parallel.hpp index dc76a0c426..50e2a058c9 100644 --- a/lib/kokkos/core/src/Qthread/Kokkos_Qthread_Parallel.hpp +++ b/lib/kokkos/core/src/Qthread/Kokkos_Qthread_Parallel.hpp @@ -61,47 +61,50 @@ namespace Impl { //---------------------------------------------------------------------------- template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelFor< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > > +class ParallelFor< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > Policy ; - const FunctorType m_func ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + typedef typename Policy::WorkRange WorkRange ; + + const FunctorType m_functor ; const Policy m_policy ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor , const Member ibeg , const Member iend ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { + for ( Member i = ibeg ; i < iend ; ++i ) { functor( i ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( ! Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor , const Member ibeg , const Member iend ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i ); + const TagType t{} ; + for ( Member i = ibeg ; i < iend ; ++i ) { + functor( t , i ); } } // Function is called once by every concurrent thread. - static void execute( QthreadExec & exec , const void * arg ) + static void exec( QthreadExec & exec , const void * arg ) { const ParallelFor & self = * ((const ParallelFor *) arg ); - driver( self.m_func , typename Policy::WorkRange( self.m_policy , exec.worker_rank() , exec.worker_size() ) ); + const WorkRange range( self.m_policy, exec.worker_rank(), exec.worker_size() ); + + ParallelFor::template exec_range< WorkTag > ( self.m_functor , range.begin() , range.end() ); // All threads wait for completion. exec.exec_all_barrier(); @@ -109,95 +112,110 @@ private: public: - ParallelFor( const FunctorType & functor - , const Policy & policy - ) - : m_func( functor ) - , m_policy( policy ) + inline + void execute() const { - Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelFor::execute , this ); + Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelFor::exec , this ); + } + + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy + ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + { } }; //---------------------------------------------------------------------------- template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelReduce< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > > +class ParallelReduce< FunctorType + , Kokkos::RangePolicy< Arg0, Arg1, Arg2, Kokkos::Qthread > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > Policy ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , typename Policy::work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , typename Policy::work_tag > ValueInit ; + + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + typedef typename Policy::WorkRange WorkRange ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; + const pointer_type m_result_ptr ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { + for ( Member i = ibeg ; i < iend ; ++i ) { functor( i , update ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( ! Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i , update ); + const TagType t{} ; + for ( Member i = ibeg ; i < iend ; ++i ) { + functor( t , i , update ); } } - static void execute( QthreadExec & exec , const void * arg ) + static void exec( QthreadExec & exec , const void * arg ) { const ParallelReduce & self = * ((const ParallelReduce *) arg ); - driver( self.m_func - , ValueInit::init( self.m_func , exec.exec_all_reduce_value() ) - , typename Policy::WorkRange( self.m_policy , exec.worker_rank() , exec.worker_size() ) - ); + const WorkRange range( self.m_policy, exec.worker_rank(), exec.worker_size() ); - exec.template exec_all_reduce( self.m_func ); + ParallelReduce::template exec_range< WorkTag >( + self.m_functor, range.begin(), range.end(), + ValueInit::init( self.m_functor , exec.exec_all_reduce_value() ) ); + + exec.template exec_all_reduce( self.m_functor ); } public: - template< class HostViewType > - ParallelReduce( const FunctorType & functor - , const Policy & policy - , const HostViewType & result_view ) - : m_func( functor ) - , m_policy( policy ) + inline + void execute() const { - QthreadExec::resize_worker_scratch( ValueTraits::value_size( m_func ) , 0 ); - - Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelReduce::execute , this ); + QthreadExec::resize_worker_scratch( ValueTraits::value_size( m_functor ) , 0 ); + Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelReduce::exec , this ); const pointer_type data = (pointer_type) QthreadExec::exec_all_reduce_result(); - Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( m_func , data ); + Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( m_functor , data ); - if ( result_view.ptr_on_device() ) { - const unsigned n = ValueTraits::value_count( m_func ); - for ( unsigned i = 0 ; i < n ; ++i ) { result_view.ptr_on_device()[i] = data[i]; } + if ( m_result_ptr ) { + const unsigned n = ValueTraits::value_count( m_functor ); + for ( unsigned i = 0 ; i < n ; ++i ) { m_result_ptr[i] = data[i]; } } } + + template< class HostViewType > + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const HostViewType & arg_result_view ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result_view.ptr_on_device() ) + { } }; //---------------------------------------------------------------------------- @@ -208,50 +226,63 @@ class ParallelFor< FunctorType , TeamPolicy< Arg0 , Arg1 , Kokkos::Qthread > > private: typedef TeamPolicy< Arg0 , Arg1 , Kokkos::Qthread > Policy ; + typedef typename Policy::member_type Member ; + typedef typename Policy::work_tag WorkTag ; - const FunctorType m_func ; - const Policy m_team ; + const FunctorType m_functor ; + const Policy m_policy ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member ) const - { m_func( member ); } + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member ) + { + while ( member ) { + functor( member ); + member.team_barrier(); + member.next_team(); + } + } template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member ) const - { m_func( TagType() , member ); } + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member ) + { + const TagType t{} ; + while ( member ) { + functor( t , member ); + member.team_barrier(); + member.next_team(); + } + } - static void execute( QthreadExec & exec , const void * arg ) + static void exec( QthreadExec & exec , const void * arg ) { const ParallelFor & self = * ((const ParallelFor *) arg ); - typename Policy::member_type member( exec , self.m_team ); - - while ( member ) { - self.ParallelFor::template driver< typename Policy::work_tag >( member ); - member.team_barrier(); - member.next_team(); - } + ParallelFor::template exec_team< WorkTag > + ( self.m_functor , Member( exec , self.m_policy ) ); exec.exec_all_barrier(); } public: - ParallelFor( const FunctorType & functor , - const Policy & policy ) - : m_func( functor ) - , m_team( policy ) + inline + void execute() const { QthreadExec::resize_worker_scratch ( /* reduction memory */ 0 - , /* team shared memory */ FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ); - - Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelFor::execute , this ); + , /* team shared memory */ FunctorTeamShmemSize< FunctorType >::value( m_functor , m_policy.team_size() ) ); + Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelFor::exec , this ); } + + ParallelFor( const FunctorType & arg_functor , + const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + { } }; //---------------------------------------------------------------------------- @@ -263,148 +294,170 @@ private: typedef TeamPolicy< Arg0 , Arg1 , Kokkos::Qthread > Policy ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , typename Policy::work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , typename Policy::work_tag > ValueInit ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - const FunctorType m_func ; - const Policy m_team ; + const FunctorType m_functor ; + const Policy m_policy ; + const pointer_type m_result_ptr ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member - , reference_type update ) const - { m_func( member , update ); } + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member , reference_type update ) + { + while ( member ) { + functor( member , update ); + member.team_barrier(); + member.next_team(); + } + } template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member - , reference_type update ) const - { m_func( TagType() , member , update ); } + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member , reference_type update ) + { + const TagType t{} ; + while ( member ) { + functor( t , member , update ); + member.team_barrier(); + member.next_team(); + } + } - static void execute( QthreadExec & exec , const void * arg ) + static void exec( QthreadExec & exec , const void * arg ) { const ParallelReduce & self = * ((const ParallelReduce *) arg ); - // Initialize thread-local value - reference_type update = ValueInit::init( self.m_func , exec.exec_all_reduce_value() ); + ParallelReduce::template exec_team< WorkTag > + ( self.m_functor + , Member( exec , self.m_policy ) + , ValueInit::init( self.m_functor , exec.exec_all_reduce_value() ) ); - typename Policy::member_type member( exec , self.m_team ); - - while ( member ) { - self.ParallelReduce::template driver< typename Policy::work_tag >( member , update ); - member.team_barrier(); - member.next_team(); - } - - exec.template exec_all_reduce< FunctorType , typename Policy::work_tag >( self.m_func ); + exec.template exec_all_reduce< FunctorType , WorkTag >( self.m_functor ); } public: - template< class ViewType > - ParallelReduce( const FunctorType & functor , - const Policy & policy , - const ViewType & result ) - : m_func( functor ) - , m_team( policy ) + inline + void execute() const { QthreadExec::resize_worker_scratch - ( /* reduction memory */ ValueTraits::value_size( functor ) - , /* team shared memory */ FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ); + ( /* reduction memory */ ValueTraits::value_size( m_functor ) + , /* team shared memory */ FunctorTeamShmemSize< FunctorType >::value( m_functor , m_policy.team_size() ) ); - Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelReduce::execute , this ); + Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelReduce::exec , this ); const pointer_type data = (pointer_type) QthreadExec::exec_all_reduce_result(); - Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( m_func , data ); + Kokkos::Impl::FunctorFinal< FunctorType , typename Policy::work_tag >::final( m_functor , data ); - const unsigned n = ValueTraits::value_count( m_func ); - for ( unsigned i = 0 ; i < n ; ++i ) { result.ptr_on_device()[i] = data[i]; } + if ( m_result_ptr ) { + const unsigned n = ValueTraits::value_count( m_functor ); + for ( unsigned i = 0 ; i < n ; ++i ) { m_result_ptr[i] = data[i]; } + } } + + template< class ViewType > + ParallelReduce( const FunctorType & arg_functor , + const Policy & arg_policy , + const ViewType & arg_result ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result.ptr_on_device() ) + { } }; //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelScan< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > > +class ParallelScan< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Qthread > Policy ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , typename Policy::work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , typename Policy::work_tag > ValueInit ; + + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + typedef typename Policy::WorkRange WorkRange ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const bool final - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update , const bool final ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { + for ( Member i = ibeg ; i < iend ; ++i ) { functor( i , update , final ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( ! Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const bool final - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend + , reference_type update , const bool final ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i , update , final ); + const TagType t{} ; + for ( Member i = ibeg ; i < iend ; ++i ) { + functor( t , i , update , final ); } } - static void execute( QthreadExec & exec , const void * arg ) + static void exec( QthreadExec & exec , const void * arg ) { const ParallelScan & self = * ((const ParallelScan *) arg ); - const typename Policy::WorkRange range( self.m_policy , exec.worker_rank() , exec.worker_size() ); + const WorkRange range( self.m_policy , exec.worker_rank() , exec.worker_size() ); // Initialize thread-local value - reference_type update = ValueInit::init( self.m_func , exec.exec_all_reduce_value() ); + reference_type update = ValueInit::init( self.m_functor , exec.exec_all_reduce_value() ); - driver( self.m_func , update , false , range ); + ParallelScan::template exec_range< WorkTag >( self.m_functor, range.begin() , range.end() , update , false ); - exec.template exec_all_scan< FunctorType , typename Policy::work_tag >( self.m_func ); + exec.template exec_all_scan< FunctorType , typename Policy::work_tag >( self.m_functor ); - driver( self.m_func , update , true , range ); + ParallelScan::template exec_range< WorkTag >( self.m_functor , range.begin() , range.end() , update , true ); exec.exec_all_barrier(); } public: - ParallelScan( const FunctorType & functor - , const Policy & policy - ) - : m_func( functor ) - , m_policy( policy ) + inline + void execute() const { - QthreadExec::resize_worker_scratch( ValueTraits::value_size( m_func ) , 0 ); + QthreadExec::resize_worker_scratch( ValueTraits::value_size( m_functor ) , 0 ); + Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelScan::exec , this ); + } - Impl::QthreadExec::exec_all( Qthread::instance() , & ParallelScan::execute , this ); + ParallelScan( const FunctorType & arg_functor + , const Policy & arg_policy + ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + { } }; diff --git a/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.cpp b/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.cpp index 9de9748de9..4f0ad49fa2 100644 --- a/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.cpp +++ b/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.cpp @@ -255,6 +255,56 @@ void Task::assign( Task ** const lhs , Task * rhs , const bool no_throw ) //---------------------------------------------------------------------------- +void Task::closeout() +{ + enum { RESPAWN = int( Kokkos::Experimental::TASK_STATE_WAITING ) | + int( Kokkos::Experimental::TASK_STATE_EXECUTING ) }; + +#if 0 +fprintf( stdout + , "worker(%d.%d) task 0x%.12lx %s\n" + , qthread_shep() + , qthread_worker_local(NULL) + , reinterpret_cast(this) + , ( m_state == RESPAWN ? "respawn" : "complete" ) + ); +fflush(stdout); +#endif + + // When dependent tasks run there would be a race + // condition between destroying this task and + // querying the active count pointer from this task. + int volatile * const active_count = m_active_count ; + + if ( m_state == RESPAWN ) { + // Task requests respawn, set state to waiting and reschedule the task + m_state = Kokkos::Experimental::TASK_STATE_WAITING ; + schedule(); + } + else { + + // Task did not respawn, is complete + m_state = Kokkos::Experimental::TASK_STATE_COMPLETE ; + + // Release dependences before allowing dependent tasks to run. + // Otherwise there is a thread race condition for removing dependences. + for ( int i = 0 ; i < m_dep_size ; ++i ) { + assign( & m_dep[i] , 0 ); + } + + // Set qthread FEB to full so that dependent tasks are allowed to execute. + // This 'task' may be deleted immediately following this function call. + qthread_fill( & m_qfeb ); + + // The dependent task could now complete and destroy 'this' task + // before the call to 'qthread_fill' returns. Therefore, for + // thread safety assume that 'this' task has now been destroyed. + } + + // Decrement active task count before returning. + Kokkos::atomic_decrement( active_count ); +} + aligned_t Task::qthread_func( void * arg ) { Task * const task = reinterpret_cast< Task * >(arg); @@ -291,62 +341,18 @@ fflush(stdout); #endif member.team_barrier(); - - close_out = member.team_rank() == 0 ; + if ( member.team_rank() == 0 ) task->closeout(); + member.team_barrier(); } else if ( task->m_apply_team && task->m_apply_single == reinterpret_cast(1) ) { // Team hard-wired to one, no cloning Kokkos::Impl::QthreadTeamPolicyMember member ; (*task->m_apply_team)( task , member ); - close_out = true ; + task->closeout(); } else { (*task->m_apply_single)( task ); - - close_out = true ; - } - - if ( close_out ) { - - // When dependent tasks run there would be a race - // condition between destroying this task and - // querying the active count pointer from this task. - int volatile * active_count = task->m_active_count ; - - if ( task->m_state == ( Kokkos::Experimental::TASK_STATE_WAITING | Kokkos::Experimental::TASK_STATE_EXECUTING ) ) { - -#if 0 -fprintf( stdout - , "worker(%d.%d) task 0x%.12lx respawn\n" - , qthread_shep() - , qthread_worker_local(NULL) - , reinterpret_cast(task) - ); -fflush(stdout); -#endif - - // Task respawned, set state to waiting and reschedule the task - task->m_state = Kokkos::Experimental::TASK_STATE_WAITING ; - task->schedule(); - } - else { - - // Task did not respawn, is complete - task->m_state = Kokkos::Experimental::TASK_STATE_COMPLETE ; - - // Release dependences before allowing dependent tasks to run. - // Otherwise there is a thread race condition for removing dependences. - for ( int i = 0 ; i < task->m_dep_size ; ++i ) { - assign( & task->m_dep[i] , 0 ); - } - - // Set qthread FEB to full so that dependent tasks are allowed to execute. - // This 'task' may be deleted immediately following this function call. - qthread_fill( & task->m_qfeb ); - } - - // Decrement active task count before returning. - Kokkos::atomic_decrement( active_count ); + task->closeout(); } #if 0 @@ -419,8 +425,7 @@ fflush(stdout); , NULL , m_dep_size , qprecon /* dependences */ , spawn_shepherd - // , unsigned( QTHREAD_SPAWN_SIMPLE | QTHREAD_SPAWN_LOCAL_PRIORITY ) - , unsigned( QTHREAD_SPAWN_LOCAL_PRIORITY ) + , unsigned( QTHREAD_SPAWN_SIMPLE | QTHREAD_SPAWN_LOCAL_PRIORITY ) , num_worker_per_shepherd - 1 ); } diff --git a/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.hpp b/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.hpp index 1f4a622ebe..9ff27de373 100644 --- a/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.hpp +++ b/lib/kokkos/core/src/Qthread/Kokkos_Qthread_TaskPolicy.hpp @@ -121,6 +121,7 @@ private: } void schedule(); + void closeout(); protected : @@ -490,7 +491,7 @@ public: KOKKOS_INLINE_FUNCTION TaskPolicy( const TaskPolicy & rhs ) : m_default_dependence_capacity( rhs.m_default_dependence_capacity ) - , m_team_size( m_team_size ) + , m_team_size( rhs.m_team_size ) , m_active_count_root(0) , m_active_count( rhs.m_active_count ) {} @@ -499,7 +500,7 @@ public: TaskPolicy( const TaskPolicy & rhs , const unsigned arg_default_dependence_capacity ) : m_default_dependence_capacity( arg_default_dependence_capacity ) - , m_team_size( m_team_size ) + , m_team_size( rhs.m_team_size ) , m_active_count_root(0) , m_active_count( rhs.m_active_count ) {} diff --git a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp index 99553fccb1..078cc658bf 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp +++ b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp @@ -1,13 +1,13 @@ /* //@HEADER // ************************************************************************ -// +// // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation -// +// // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,7 +36,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) -// +// // ************************************************************************ //@HEADER */ @@ -50,9 +50,7 @@ #include #include #include -#include -#include -#include +#include #include @@ -135,7 +133,11 @@ void ThreadsExec::driver(void) ThreadsExec::ThreadsExec() : m_pool_base(0) +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) , m_scratch() +#else + , m_scratch(0) +#endif , m_scratch_reduce_end(0) , m_scratch_thread_end(0) , m_numa_rank(0) @@ -194,8 +196,25 @@ ThreadsExec::~ThreadsExec() { const unsigned entry = m_pool_size - ( m_pool_rank + 1 ); - m_pool_base = 0 ; +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::HostSpace , void > Record ; + + if ( m_scratch ) { + Record * const r = Record::get_record( m_scratch ); + + m_scratch = 0 ; + + Record::decrement( r ); + } + +#else + m_scratch.clear(); + +#endif + + m_pool_base = 0 ; m_scratch_reduce_end = 0 ; m_scratch_thread_end = 0 ; m_numa_rank = 0 ; @@ -303,6 +322,10 @@ void ThreadsExec::fence() s_current_function = 0 ; s_current_function_arg = 0 ; + + // Make sure function and arguments are cleared before + // potentially re-activating threads with a subsequent launch. + memory_fence(); } /** \brief Begin execution of the asynchronous functor */ @@ -317,6 +340,9 @@ void ThreadsExec::start( void (*func)( ThreadsExec & , const void * ) , const vo s_current_function = func ; s_current_function_arg = arg ; + // Make sure function and arguments are written before activating threads. + memory_fence(); + // Activate threads: for ( int i = s_thread_pool_size[0] ; 0 < i-- ; ) { s_threads_exec[i]->m_pool_state = ThreadsExec::Active ; @@ -376,6 +402,9 @@ void ThreadsExec::execute_serial( void (*func)( ThreadsExec & , const void * ) ) s_current_function = func ; s_current_function_arg = & s_threads_process ; + // Make sure function and arguments are written before activating threads. + memory_fence(); + const unsigned begin = s_threads_process.m_pool_base ? 1 : 0 ; for ( unsigned i = s_thread_pool_size[0] ; begin < i ; ) { @@ -394,6 +423,9 @@ void ThreadsExec::execute_serial( void (*func)( ThreadsExec & , const void * ) ) s_current_function_arg = 0 ; s_current_function = 0 ; + + // Make sure function and arguments are cleared before proceeding. + memory_fence(); } //---------------------------------------------------------------------------- @@ -405,17 +437,51 @@ void * ThreadsExec::root_reduce_scratch() void ThreadsExec::execute_resize_scratch( ThreadsExec & exec , const void * ) { +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + + typedef Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::HostSpace , void > Record ; + + if ( exec.m_scratch ) { + Record * const r = Record::get_record( exec.m_scratch ); + + exec.m_scratch = 0 ; + + Record::decrement( r ); + } + +#else + exec.m_scratch.clear(); +#endif + exec.m_scratch_reduce_end = s_threads_process.m_scratch_reduce_end ; exec.m_scratch_thread_end = s_threads_process.m_scratch_thread_end ; if ( s_threads_process.m_scratch_thread_end ) { +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + + // Allocate tracked memory: + { + Record * const r = Record::allocate( Kokkos::HostSpace() , "thread_scratch" , s_threads_process.m_scratch_thread_end ); + + Record::increment( r ); + + exec.m_scratch = r->data(); + } + + unsigned * ptr = reinterpret_cast( exec.m_scratch ); + +#else + exec.m_scratch = HostSpace::allocate_and_track( "thread_scratch" , s_threads_process.m_scratch_thread_end ); unsigned * ptr = reinterpret_cast( exec.m_scratch.alloc_ptr() ); + +#endif + unsigned * const end = ptr + s_threads_process.m_scratch_thread_end / sizeof(unsigned); // touch on this thread @@ -452,7 +518,11 @@ void * ThreadsExec::resize_scratch( size_t reduce_size , size_t thread_size ) s_threads_process.m_scratch = s_threads_exec[0]->m_scratch ; } +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + return s_threads_process.m_scratch ; +#else return s_threads_process.m_scratch.alloc_ptr() ; +#endif } //---------------------------------------------------------------------------- @@ -550,7 +620,8 @@ void ThreadsExec::initialize( unsigned thread_count , // then they will be given default values based upon hwloc detection // and allowed asynchronous execution. - const bool hwloc_avail = hwloc::available(); + const bool hwloc_avail = Kokkos::hwloc::available(); + const bool hwloc_can_bind = hwloc_avail && Kokkos::hwloc::can_bind_threads(); if ( thread_count == 0 ) { thread_count = hwloc_avail @@ -588,7 +659,11 @@ void ThreadsExec::initialize( unsigned thread_count , // If hwloc available then spawned thread will // choose its own entry in 's_threads_coord' // otherwise specify the entry. - s_current_function_arg = (void*)static_cast( hwloc_avail ? ~0u : ith ); + s_current_function_arg = (void*)static_cast( hwloc_can_bind ? ~0u : ith ); + + // Make sure all outstanding memory writes are complete + // before spawning the new thread. + memory_fence(); // Spawn thread executing the 'driver()' function. // Wait until spawned thread has attempted to initialize. @@ -617,9 +692,13 @@ void ThreadsExec::initialize( unsigned thread_count , s_current_function_arg = 0 ; s_threads_process.m_pool_state = ThreadsExec::Inactive ; + memory_fence(); + if ( ! thread_spawn_failed ) { // Bind process to the core on which it was located before spawning occured - Kokkos::hwloc::bind_this_thread( proc_coord ); + if (hwloc_can_bind) { + Kokkos::hwloc::bind_this_thread( proc_coord ); + } if ( thread_spawn_begin ) { // Include process in pool. const std::pair coord = Kokkos::hwloc::get_this_thread_coordinate(); @@ -702,7 +781,9 @@ void ThreadsExec::finalize() s_threads_exec[0] = 0 ; } - Kokkos::hwloc::unbind_this_thread(); + if (Kokkos::hwloc::can_bind_threads() ) { + Kokkos::hwloc::unbind_this_thread(); + } s_thread_pool_size[0] = 0 ; s_thread_pool_size[1] = 0 ; diff --git a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp index 3820697977..684eac8b7d 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp +++ b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp @@ -89,7 +89,11 @@ private: ThreadsExec * const * m_pool_base ; ///< Base for pool fan-in +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) Impl::AllocationTracker m_scratch ; +#else + void * m_scratch ; +#endif int m_scratch_reduce_end ; int m_scratch_thread_end ; int m_numa_rank ; @@ -122,9 +126,19 @@ public: static int get_thread_count(); static ThreadsExec * get_thread( const int init_thread_rank ); +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + inline void * reduce_memory() const { return reinterpret_cast(m_scratch.alloc_ptr()); } KOKKOS_INLINE_FUNCTION void * scratch_memory() const { return reinterpret_cast(m_scratch.alloc_ptr()) + m_scratch_reduce_end ; } +#else + + inline void * reduce_memory() const { return m_scratch ; } + KOKKOS_INLINE_FUNCTION void * scratch_memory() const + { return reinterpret_cast(m_scratch) + m_scratch_reduce_end ; } + +#endif + KOKKOS_INLINE_FUNCTION int volatile & state() { return m_pool_state ; } KOKKOS_INLINE_FUNCTION ThreadsExec * const * pool_base() const { return m_pool_base ; } diff --git a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp index 40d5efd0fe..ce09248678 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp +++ b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp @@ -155,6 +155,7 @@ void ThreadsExec::wait_yield( volatile int & flag , const int value ) #elif defined( KOKKOS_HAVE_WINTHREAD ) /* Windows libraries */ +#include #include #include diff --git a/lib/kokkos/core/src/Threads/Kokkos_ThreadsTeam.hpp b/lib/kokkos/core/src/Threads/Kokkos_ThreadsTeam.hpp index 53b5eb01df..b69d72d78c 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_ThreadsTeam.hpp +++ b/lib/kokkos/core/src/Threads/Kokkos_ThreadsTeam.hpp @@ -423,6 +423,8 @@ private: int m_team_size ; int m_team_alloc ; + size_t m_scratch_size; + inline void init( const int league_size_request , const int team_size_request ) @@ -477,19 +479,68 @@ public: inline int team_size() const { return m_team_size ; } inline int team_alloc() const { return m_team_alloc ; } inline int league_size() const { return m_league_size ; } + inline size_t scratch_size() const { return m_scratch_size ; } /** \brief Specify league size, request team size */ - TeamPolicy( execution_space & , int league_size_request , int team_size_request , int vector_length_request = 1 ) + TeamPolicy( execution_space & + , int league_size_request + , int team_size_request + , int vector_length_request = 1 ) : m_league_size(0) , m_team_size(0) , m_team_alloc(0) + , m_scratch_size ( 0 ) { init(league_size_request,team_size_request); (void) vector_length_request; } - TeamPolicy( int league_size_request , int team_size_request , int vector_length_request = 1 ) + /** \brief Specify league size, request team size */ + TeamPolicy( execution_space & + , int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , int /* vector_length_request */ = 1 ) : m_league_size(0) , m_team_size(0) , m_team_alloc(0) - { init(league_size_request,team_size_request); (void) vector_length_request; } + , m_scratch_size ( 0 ) + { init(league_size_request,execution_space::thread_pool_size(2)); } + + TeamPolicy( int league_size_request + , int team_size_request + , int /* vector_length_request */ = 1 ) + : m_league_size(0) + , m_team_size(0) + , m_team_alloc(0) + , m_scratch_size ( 0 ) + { init(league_size_request,team_size_request); } + + TeamPolicy( int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , int /* vector_length_request */ = 1 ) + : m_league_size(0) + , m_team_size(0) + , m_team_alloc(0) + , m_scratch_size ( 0 ) + { init(league_size_request,execution_space::thread_pool_size(2)); } + + template + TeamPolicy( int league_size_request + , int team_size_request + , const Experimental::TeamScratchRequest & scratch_request ) + : m_league_size(0) + , m_team_size(0) + , m_team_alloc(0) + , m_scratch_size(scratch_request.total(team_size_request)) + { init(league_size_request,team_size_request); } + + + template + TeamPolicy( int league_size_request + , const Kokkos::AUTO_t & /* team_size_request */ + , const Experimental::TeamScratchRequest & scratch_request ) + : m_league_size(0) + , m_team_size(0) + , m_team_alloc(0) + , m_scratch_size(scratch_request.total(execution_space::thread_pool_size(2))) + { init(league_size_request,execution_space::thread_pool_size(2)); } typedef Impl::ThreadsExecTeamMember member_type ; diff --git a/lib/kokkos/core/src/Threads/Kokkos_Threads_Parallel.hpp b/lib/kokkos/core/src/Threads/Kokkos_Threads_Parallel.hpp index 4b2a169126..9e3b0acd3d 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_Threads_Parallel.hpp +++ b/lib/kokkos/core/src/Threads/Kokkos_Threads_Parallel.hpp @@ -45,6 +45,7 @@ #define KOKKOS_THREADS_PARALLEL_HPP #include +#include #include @@ -58,363 +59,440 @@ namespace Impl { //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +/* ParallelFor Kokkos::Threads with RangePolicy */ template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelFor< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > > +class ParallelFor< FunctorType + , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > + > { private: typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > Policy ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::member_type Member ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { + #if defined( KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION ) && \ + defined( KOKKOS_HAVE_PRAGMA_IVDEP ) + #pragma ivdep + #endif + for ( Member i = ibeg ; i < iend ; ++i ) { functor( i ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( ! Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member ibeg , const Member iend ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i ); + const TagType t{} ; + #if defined( KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION ) && \ + defined( KOKKOS_HAVE_PRAGMA_IVDEP ) + #pragma ivdep + #endif + for ( Member i = ibeg ; i < iend ; ++i ) { + functor( t , i ); } } - static void execute( ThreadsExec & exec , const void * arg ) + static void exec( ThreadsExec & exec , const void * arg ) { const ParallelFor & self = * ((const ParallelFor *) arg ); - driver( self.m_func , typename Policy::WorkRange( self.m_policy , exec.pool_rank() , exec.pool_size() ) ); + WorkRange range( self.m_policy , exec.pool_rank() , exec.pool_size() ); + + ParallelFor::template exec_range< WorkTag > + ( self.m_functor , range.begin() , range.end() ); exec.fan_in(); } public: - ParallelFor( const FunctorType & functor - , const Policy & policy ) - : m_func( functor ) - , m_policy( policy ) + inline + void execute() const { - ThreadsExec::start( & ParallelFor::execute , this ); - + ThreadsExec::start( & ParallelFor::exec , this ); ThreadsExec::fence(); } + + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + {} }; +//---------------------------------------------------------------------------- +/* ParallelFor Kokkos::Threads with TeamPolicy */ + template< class FunctorType , class Arg0 , class Arg1 > -class ParallelFor< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > > +class ParallelFor< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > + > { private: typedef TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > Policy ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; const int m_shared ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member ) const - { m_func( member ); } + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member ) + { + for ( ; member.valid() ; member.next() ) { + functor( member ); + } + } template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member ) const - { m_func( TagType() , member ); } + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member ) + { + const TagType t{} ; + for ( ; member.valid() ; member.next() ) { + functor( t , member ); + } + } - static void execute( ThreadsExec & exec , const void * arg ) + static void exec( ThreadsExec & exec , const void * arg ) { const ParallelFor & self = * ((const ParallelFor *) arg ); - typename Policy::member_type member( & exec , self.m_policy , self.m_shared ); - - for ( ; member.valid() ; member.next() ) { - self.ParallelFor::template driver< typename Policy::work_tag >( member ); - } + ParallelFor::exec_team< WorkTag > + ( self.m_functor , Member( & exec , self.m_policy , self.m_shared ) ); exec.fan_in(); } public: - ParallelFor( const FunctorType & functor - , const Policy & policy ) - : m_func( functor ) - , m_policy( policy ) - , m_shared( FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ) + inline + void execute() const { ThreadsExec::resize_scratch( 0 , Policy::member_type::team_reduce_size() + m_shared ); - ThreadsExec::start( & ParallelFor::execute , this ); + ThreadsExec::start( & ParallelFor::exec , this ); ThreadsExec::fence(); } + + ParallelFor( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_shared( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( arg_functor , arg_policy.team_size() ) ) + { } }; - - //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +/* ParallelReduce with Kokkos::Threads and RangePolicy */ template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelReduce< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > > +class ParallelReduce< FunctorType + , Kokkos::RangePolicy< Arg0, Arg1, Arg2, Kokkos::Threads > + > { private: - typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > Policy ; - typedef typename Policy::work_tag work_tag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , work_tag > ValueInit ; + typedef Kokkos::RangePolicy< Arg0 , Arg1, Arg2, Kokkos::Threads > Policy ; + + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::member_type Member ; + + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; + const pointer_type m_result_ptr ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member & ibeg , const Member & iend + , reference_type update ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { + #if defined( KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION ) && \ + defined( KOKKOS_HAVE_PRAGMA_IVDEP ) + #pragma ivdep + #endif + for ( Member i = ibeg ; i < iend ; ++i ) { functor( i , update ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( ! Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member & ibeg , const Member & iend + , reference_type update ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i , update ); + const TagType t{} ; + #if defined( KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION ) && \ + defined( KOKKOS_HAVE_PRAGMA_IVDEP ) + #pragma ivdep + #endif + for ( Member i = ibeg ; i < iend ; ++i ) { + functor( t , i , update ); } } - static void execute( ThreadsExec & exec , const void * arg ) + static void exec( ThreadsExec & exec , const void * arg ) { const ParallelReduce & self = * ((const ParallelReduce *) arg ); + const WorkRange range( self.m_policy, exec.pool_rank(), exec.pool_size() ); - driver( self.m_func - , ValueInit::init( self.m_func , exec.reduce_memory() ) - , typename Policy::WorkRange( self.m_policy , exec.pool_rank() , exec.pool_size() ) - ); + ParallelReduce::template exec_range< WorkTag > + ( self.m_functor , range.begin() , range.end() + , ValueInit::init( self.m_functor , exec.reduce_memory() ) ); - exec.template fan_in_reduce< FunctorType , work_tag >( self.m_func ); + exec.template fan_in_reduce< FunctorType , WorkTag >( self.m_functor ); } public: - template< class HostViewType > - ParallelReduce( const FunctorType & functor , - const Policy & policy , - const HostViewType & result_view ) - : m_func( functor ) - , m_policy( policy ) + inline + void execute() const { - ThreadsExec::resize_scratch( ValueTraits::value_size( m_func ) , 0 ); + ThreadsExec::resize_scratch( ValueTraits::value_size( m_functor ) , 0 ); - ThreadsExec::start( & ParallelReduce::execute , this ); - - const pointer_type data = (pointer_type) ThreadsExec::root_reduce_scratch(); + ThreadsExec::start( & ParallelReduce::exec , this ); ThreadsExec::fence(); - if ( result_view.ptr_on_device() ) { - const unsigned n = ValueTraits::value_count( m_func ); - for ( unsigned i = 0 ; i < n ; ++i ) { result_view.ptr_on_device()[i] = data[i]; } + if ( m_result_ptr ) { + + const pointer_type data = + (pointer_type) ThreadsExec::root_reduce_scratch(); + + const unsigned n = ValueTraits::value_count( m_functor ); + for ( unsigned i = 0 ; i < n ; ++i ) { m_result_ptr[i] = data[i]; } } } + + template< class HostViewType > + ParallelReduce( const FunctorType & arg_functor , + const Policy & arg_policy , + const HostViewType & arg_result_view ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result_view.ptr_on_device() ) + { + static_assert( Kokkos::is_view< HostViewType >::value + , "Kokkos::Threads reduce result must be a View" ); + + static_assert( std::is_same< typename HostViewType::memory_space , HostSpace >::value + , "Kokkos::Threads reduce result must be a View in HostSpace" ); + } }; //---------------------------------------------------------------------------- +/* ParallelReduce with Kokkos::Threads and TeamPolicy */ template< class FunctorType , class Arg0 , class Arg1 > -class ParallelReduce< FunctorType , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > > +class ParallelReduce< FunctorType + , Kokkos::TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > + > { private: - typedef TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > Policy ; - typedef typename Policy::work_tag work_tag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , work_tag > ValueInit ; + typedef TeamPolicy< Arg0 , Arg1 , Kokkos::Threads > Policy ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; + const pointer_type m_result_ptr ; const int m_shared ; template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member - , reference_type update ) const - { m_func( member , update ); } + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member , reference_type update ) + { + for ( ; member.valid() ; member.next() ) { + functor( member , update ); + } + } template< class TagType > - KOKKOS_FORCEINLINE_FUNCTION - void driver( typename Impl::enable_if< ! Impl::is_same< TagType , void >::value , - const typename Policy::member_type & >::type member - , reference_type update ) const - { m_func( TagType() , member , update ); } + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_team( const FunctorType & functor , Member member , reference_type update ) + { + const TagType t{} ; + for ( ; member.valid() ; member.next() ) { + functor( t , member , update ); + } + } - static void execute( ThreadsExec & exec , const void * arg ) + static void exec( ThreadsExec & exec , const void * arg ) { const ParallelReduce & self = * ((const ParallelReduce *) arg ); - // Initialize thread-local value - reference_type update = ValueInit::init( self.m_func , exec.reduce_memory() ); + ParallelReduce::template exec_team< WorkTag > + ( self.m_functor , Member( & exec , self.m_policy , self.m_shared ) + , ValueInit::init( self.m_functor , exec.reduce_memory() ) ); - typename Policy::member_type member( & exec , self.m_policy , self.m_shared ); - for ( ; member.valid() ; member.next() ) { - self.ParallelReduce::template driver< work_tag >( member , update ); - } - - exec.template fan_in_reduce< FunctorType , work_tag >( self.m_func ); + exec.template fan_in_reduce< FunctorType , WorkTag >( self.m_functor ); } public: - ParallelReduce( const FunctorType & functor - , const Policy & policy ) - : m_func( functor ) - , m_policy( policy ) - , m_shared( FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ) + inline + void execute() const { - ThreadsExec::resize_scratch( ValueTraits::value_size( m_func ) , Policy::member_type::team_reduce_size() + m_shared ); + ThreadsExec::resize_scratch( ValueTraits::value_size( m_functor ) , Policy::member_type::team_reduce_size() + m_shared ); - ThreadsExec::start( & ParallelReduce::execute , this ); + ThreadsExec::start( & ParallelReduce::exec , this ); ThreadsExec::fence(); + + if ( m_result_ptr ) { + + const pointer_type data = (pointer_type) ThreadsExec::root_reduce_scratch(); + + const unsigned n = ValueTraits::value_count( m_functor ); + for ( unsigned i = 0 ; i < n ; ++i ) { m_result_ptr[i] = data[i]; } + } } template< class ViewType > - ParallelReduce( const FunctorType & functor - , const Policy & policy - , const ViewType & result ) - : m_func( functor ) - , m_policy( policy ) - , m_shared( FunctorTeamShmemSize< FunctorType >::value( functor , policy.team_size() ) ) - { - ThreadsExec::resize_scratch( ValueTraits::value_size( m_func ) , Policy::member_type::team_reduce_size() + m_shared ); - - ThreadsExec::start( & ParallelReduce::execute , this ); - - const pointer_type data = (pointer_type) ThreadsExec::root_reduce_scratch(); - - ThreadsExec::fence(); - - const unsigned n = ValueTraits::value_count( m_func ); - for ( unsigned i = 0 ; i < n ; ++i ) { result.ptr_on_device()[i] = data[i]; } - } + ParallelReduce( const FunctorType & arg_functor + , const Policy & arg_policy + , const ViewType & arg_result ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + , m_result_ptr( arg_result.ptr_on_device() ) + , m_shared( arg_policy.scratch_size() + FunctorTeamShmemSize< FunctorType >::value( arg_functor , arg_policy.team_size() ) ) + { } }; //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +/* ParallelScan with Kokkos::Threads and RangePolicy */ template< class FunctorType , class Arg0 , class Arg1 , class Arg2 > -class ParallelScan< FunctorType , Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > > +class ParallelScan< FunctorType + , Kokkos::RangePolicy< Arg0, Arg1, Arg2, Kokkos::Threads > + > { private: - typedef Kokkos::RangePolicy< Arg0 , Arg1 , Arg2 , Kokkos::Threads > Policy ; - typedef typename Policy::work_tag work_tag ; - typedef Kokkos::Impl::FunctorValueTraits< FunctorType , work_tag > ValueTraits ; - typedef Kokkos::Impl::FunctorValueInit< FunctorType , work_tag > ValueInit ; + typedef Kokkos::RangePolicy< Arg0, Arg1, Arg2, Kokkos::Threads > Policy ; + typedef typename Policy::WorkRange WorkRange ; + typedef typename Policy::work_tag WorkTag ; + typedef typename Policy::member_type Member ; + typedef Kokkos::Impl::FunctorValueTraits< FunctorType, WorkTag > ValueTraits ; + typedef Kokkos::Impl::FunctorValueInit< FunctorType, WorkTag > ValueInit ; typedef typename ValueTraits::pointer_type pointer_type ; typedef typename ValueTraits::reference_type reference_type ; - const FunctorType m_func ; + const FunctorType m_functor ; const Policy m_policy ; - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const bool final - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member & ibeg , const Member & iend + , reference_type update , const bool final ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { + #if defined( KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION ) && \ + defined( KOKKOS_HAVE_PRAGMA_IVDEP ) + #pragma ivdep + #endif + for ( Member i = ibeg ; i < iend ; ++i ) { functor( i , update , final ); } } - template< class PType > - KOKKOS_FORCEINLINE_FUNCTION static - void driver( typename Impl::enable_if< - ( ! Impl::is_same< typename PType::work_tag , void >::value ) - , const FunctorType & >::type functor - , reference_type update - , const bool final - , const PType & range ) + template< class TagType > + inline static + typename std::enable_if< ! std::is_same< TagType , void >::value >::type + exec_range( const FunctorType & functor + , const Member & ibeg , const Member & iend + , reference_type update , const bool final ) { - const typename PType::member_type e = range.end(); - for ( typename PType::member_type i = range.begin() ; i < e ; ++i ) { - functor( typename PType::work_tag() , i , update , final ); + const TagType t{} ; + #if defined( KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION ) && \ + defined( KOKKOS_HAVE_PRAGMA_IVDEP ) + #pragma ivdep + #endif + for ( Member i = ibeg ; i < iend ; ++i ) { + functor( t , i , update , final ); } } - static void execute( ThreadsExec & exec , const void * arg ) + static void exec( ThreadsExec & exec , const void * arg ) { const ParallelScan & self = * ((const ParallelScan *) arg ); - const typename Policy::WorkRange range( self.m_policy , exec.pool_rank() , exec.pool_size() ); + const WorkRange range( self.m_policy, exec.pool_rank(), exec.pool_size() ); - reference_type update = ValueInit::init( self.m_func , exec.reduce_memory() ); + reference_type update = + ValueInit::init( self.m_functor , exec.reduce_memory() ); - driver( self.m_func , update , false , range ); + ParallelScan::template exec_range< WorkTag > + ( self.m_functor , range.begin(), range.end(), update, false ); - // exec.scan_large( self.m_func ); - exec.template scan_small( self.m_func ); + // exec.template scan_large( self.m_functor ); + exec.template scan_small( self.m_functor ); - driver( self.m_func , update , true , range ); + ParallelScan::template exec_range< WorkTag > + ( self.m_functor , range.begin(), range.end(), update, true ); exec.fan_in(); } public: - ParallelScan( const FunctorType & functor , const Policy & policy ) - : m_func( functor ) - , m_policy( policy ) + inline + void execute() const { - ThreadsExec::resize_scratch( 2 * ValueTraits::value_size( m_func ) , 0 ); - ThreadsExec::start( & ParallelScan::execute , this ); + ThreadsExec::resize_scratch( 2 * ValueTraits::value_size( m_functor ) , 0 ); + ThreadsExec::start( & ParallelScan::exec , this ); ThreadsExec::fence(); } + + ParallelScan( const FunctorType & arg_functor + , const Policy & arg_policy ) + : m_functor( arg_functor ) + , m_policy( arg_policy ) + { } }; } // namespace Impl diff --git a/lib/kokkos/core/src/impl/CMakeLists.txt b/lib/kokkos/core/src/impl/CMakeLists.txt new file mode 100644 index 0000000000..c543194de3 --- /dev/null +++ b/lib/kokkos/core/src/impl/CMakeLists.txt @@ -0,0 +1,18 @@ + +SET(HEADERS "") +SET(SOURCES "") + +FILE(GLOB HEADERS *.hpp) +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_LIBRARY( + kokkoscore_impl + NOINSTALLHEADERS ${HEADERS} + SOURCES ${SOURCES} + DEPLIBS + ) + +SET(TRILINOS_INCDIR ${CMAKE_INSTALL_PREFIX}/${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}) + +INSTALL(FILES ${HEADERS} DESTINATION ${TRILINOS_INCDIR}/impl/) + diff --git a/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.cpp b/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.cpp index 50168fe3cc..e14929d163 100644 --- a/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.cpp +++ b/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.cpp @@ -47,6 +47,27 @@ namespace Kokkos { namespace Experimental { namespace Impl { +int SharedAllocationRecord< void , void >::s_tracking_enabled = 1 ; + +void SharedAllocationRecord< void , void >::tracking_claim_and_disable() +{ + // A host thread claim and disable tracking flag + + while ( ! Kokkos::atomic_compare_exchange_strong( & s_tracking_enabled, 1, 0 ) ); +} + +void SharedAllocationRecord< void , void >::tracking_release_and_enable() +{ + // The host thread that claimed and disabled the tracking flag + // now release and enable tracking. + + if ( ! Kokkos::atomic_compare_exchange_strong( & s_tracking_enabled, 0, 1 ) ){ + Kokkos::Impl::throw_runtime_exception("Kokkos::Experimental::Impl::SharedAllocationRecord<>::tracking_release_and_enable FAILED, this host process thread did not hold the lock" ); + } +} + +//---------------------------------------------------------------------------- + bool SharedAllocationRecord< void , void >:: is_sane( SharedAllocationRecord< void , void > * arg_record ) @@ -61,7 +82,7 @@ is_sane( SharedAllocationRecord< void , void > * arg_record ) SharedAllocationRecord * root_next = 0 ; // Lock the list: - while ( ( root_next = Kokkos::atomic_exchange( & root->m_next , zero ) ) == 0 ); + while ( ( root_next = Kokkos::atomic_exchange( & root->m_next , zero ) ) == zero ); for ( SharedAllocationRecord * rec = root_next ; ok && rec != root ; rec = rec->m_next ) { const bool ok_non_null = rec && rec->m_prev && ( rec == root || rec->m_next ); @@ -73,14 +94,25 @@ is_sane( SharedAllocationRecord< void , void > * arg_record ) ok = ok_root && ok_prev_next && ok_next_prev && ok_count ; if ( ! ok ) { - fprintf(stderr,"Kokkos::Experimental::Impl::SharedAllocationRecord failed is_sane: rec(0x%.12lx){ m_count(%d) m_root(0x%.12lx) m_next(0x%.12lx) m_prev(0x%.12lx) m_next->m_prev(0x%.12lx) m_prev->m_next(0x%.12lx) }\n" - , reinterpret_cast< unsigned long >( rec ) + //Formatting dependent on sizeof(uintptr_t) + const char * format_string; + + if (sizeof(uintptr_t) == sizeof(unsigned long)) { + format_string = "Kokkos::Experimental::Impl::SharedAllocationRecord failed is_sane: rec(0x%.12lx){ m_count(%d) m_root(0x%.12lx) m_next(0x%.12lx) m_prev(0x%.12lx) m_next->m_prev(0x%.12lx) m_prev->m_next(0x%.12lx) }\n"; + } + else if (sizeof(uintptr_t) == sizeof(unsigned long long)) { + format_string = "Kokkos::Experimental::Impl::SharedAllocationRecord failed is_sane: rec(0x%.12llx){ m_count(%d) m_root(0x%.12llx) m_next(0x%.12llx) m_prev(0x%.12llx) m_next->m_prev(0x%.12llx) m_prev->m_next(0x%.12llx) }\n"; + } + + fprintf(stderr + , format_string + , reinterpret_cast< uintptr_t >( rec ) , rec->m_count - , reinterpret_cast< unsigned long >( rec->m_root ) - , reinterpret_cast< unsigned long >( rec->m_next ) - , reinterpret_cast< unsigned long >( rec->m_prev ) - , reinterpret_cast< unsigned long >( rec->m_next->m_prev ) - , reinterpret_cast< unsigned long >( rec->m_prev != rec->m_root ? rec->m_prev->m_next : root_next ) + , reinterpret_cast< uintptr_t >( rec->m_root ) + , reinterpret_cast< uintptr_t >( rec->m_next ) + , reinterpret_cast< uintptr_t >( rec->m_prev ) + , reinterpret_cast< uintptr_t >( rec->m_next->m_prev ) + , reinterpret_cast< uintptr_t >( rec->m_prev != rec->m_root ? rec->m_prev->m_next : root_next ) ); } @@ -102,7 +134,7 @@ SharedAllocationRecord::find( SharedAllocationRecord * con SharedAllocationRecord * root_next = 0 ; // Lock the list: - while ( ( root_next = Kokkos::atomic_exchange( & arg_root->m_next , 0 ) ) == 0 ); + while ( ( root_next = Kokkos::atomic_exchange( & arg_root->m_next , zero ) ) == zero ); // Iterate searching for the record with this data pointer @@ -148,7 +180,7 @@ SharedAllocationRecord( SharedAllocationRecord * arg_root m_prev = m_root ; // Read root->m_next and lock by setting to zero - while ( ( m_next = Kokkos::atomic_exchange( & m_root->m_next , zero ) ) == 0 ); + while ( ( m_next = Kokkos::atomic_exchange( & m_root->m_next , zero ) ) == zero ); m_next->m_prev = this ; @@ -187,7 +219,7 @@ decrement( SharedAllocationRecord< void , void > * arg_record ) SharedAllocationRecord * root_next = 0 ; // Lock the list: - while ( ( root_next = Kokkos::atomic_exchange( & arg_record->m_root->m_next , 0 ) ) == 0 ); + while ( ( root_next = Kokkos::atomic_exchange( & arg_record->m_root->m_next , zero ) ) == zero ); arg_record->m_next->m_prev = arg_record->m_prev ; @@ -232,16 +264,26 @@ print_host_accessible_records( std::ostream & s if ( detail ) { do { + //Formatting dependent on sizeof(uintptr_t) + const char * format_string; - snprintf( buffer , 256 , "%s addr( 0x%.12lx ) list( 0x%.12lx 0x%.12lx ) extent[ 0x%.12lx + %.8ld ] count(%d) dealloc(0x%.12lx) %s\n" + if (sizeof(uintptr_t) == sizeof(unsigned long)) { + format_string = "%s addr( 0x%.12lx ) list( 0x%.12lx 0x%.12lx ) extent[ 0x%.12lx + %.8ld ] count(%d) dealloc(0x%.12lx) %s\n"; + } + else if (sizeof(uintptr_t) == sizeof(unsigned long long)) { + format_string = "%s addr( 0x%.12llx ) list( 0x%.12llx 0x%.12llx ) extent[ 0x%.12llx + %.8ld ] count(%d) dealloc(0x%.12llx) %s\n"; + } + + snprintf( buffer , 256 + , format_string , space_name - , reinterpret_cast( r ) - , reinterpret_cast( r->m_prev ) - , reinterpret_cast( r->m_next ) - , reinterpret_cast( r->m_alloc_ptr ) + , reinterpret_cast( r ) + , reinterpret_cast( r->m_prev ) + , reinterpret_cast( r->m_next ) + , reinterpret_cast( r->m_alloc_ptr ) , r->m_alloc_size , r->m_count - , reinterpret_cast( r->m_dealloc ) + , reinterpret_cast( r->m_dealloc ) , r->m_alloc_ptr->m_label ); std::cout << buffer ; @@ -251,10 +293,20 @@ print_host_accessible_records( std::ostream & s else { do { if ( r->m_alloc_ptr ) { + //Formatting dependent on sizeof(uintptr_t) + const char * format_string; - snprintf( buffer , 256 , "%s [ 0x%.12lx + %ld ] %s\n" + if (sizeof(uintptr_t) == sizeof(unsigned long)) { + format_string = "%s [ 0x%.12lx + %ld ] %s\n"; + } + else if (sizeof(uintptr_t) == sizeof(unsigned long long)) { + format_string = "%s [ 0x%.12llx + %ld ] %s\n"; + } + + snprintf( buffer , 256 + , format_string , space_name - , reinterpret_cast< unsigned long >( r->data() ) + , reinterpret_cast< uintptr_t >( r->data() ) , r->size() , r->m_alloc_ptr->m_label ); diff --git a/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.hpp b/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.hpp index c8c553731a..f6fbe0b374 100644 --- a/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.hpp +++ b/lib/kokkos/core/src/impl/KokkosExp_SharedAlloc.hpp @@ -41,6 +41,9 @@ //@HEADER */ +#ifndef KOKKOS_SHARED_ALLOC_HPP_ +#define KOKKOS_SHARED_ALLOC_HPP_ + namespace Kokkos { namespace Experimental { namespace Impl { @@ -78,6 +81,8 @@ protected: typedef void (* function_type )( SharedAllocationRecord * ); + static int s_tracking_enabled ; + SharedAllocationHeader * const m_alloc_ptr ; size_t const m_alloc_size ; function_type const m_dealloc ; @@ -100,6 +105,18 @@ protected: public: + static int tracking_enabled() { return s_tracking_enabled ; } + + /**\brief A host process thread claims and disables the + * shared allocation tracking flag. + */ + static void tracking_claim_and_disable(); + + /**\brief A host process thread releases and enables the + * shared allocation tracking flag. + */ + static void tracking_release_and_enable(); + ~SharedAllocationRecord() = default ; constexpr SharedAllocationRecord() @@ -148,6 +165,25 @@ public: , const bool detail ); }; +namespace { + +/* Taking the address of this function so make sure it is unique */ +template < class MemorySpace , class DestroyFunctor > +void deallocate( SharedAllocationRecord * record_ptr ) +{ + typedef SharedAllocationRecord< MemorySpace , void > base_type ; + typedef SharedAllocationRecord< MemorySpace , DestroyFunctor > this_type ; + + this_type * const ptr = static_cast< this_type * >( + static_cast< base_type * >( record_ptr ) ); + + ptr->m_destroy.destroy_shared_allocation(); + + delete ptr ; +} + +} + /* * Memory space specialization of SharedAllocationRecord< Space , void > requires : * @@ -158,25 +194,23 @@ public: * Space m_space ; * } */ - template< class MemorySpace , class DestroyFunctor > class SharedAllocationRecord : public SharedAllocationRecord< MemorySpace , void > { private: - static void deallocate( SharedAllocationRecord * record_ptr ) - { delete static_cast*>(record_ptr); } - SharedAllocationRecord( const MemorySpace & arg_space , const std::string & arg_label , const size_t arg_alloc ) /* Allocate user memory as [ SharedAllocationHeader , user_memory ] */ - : SharedAllocationRecord< MemorySpace , void >( arg_space , arg_label , arg_alloc , & deallocate ) + : SharedAllocationRecord< MemorySpace , void >( arg_space , arg_label , arg_alloc , & Kokkos::Experimental::Impl::deallocate< MemorySpace , DestroyFunctor > ) , m_destroy() {} - ~SharedAllocationRecord() { m_destroy.destroy_shared_allocation(); } + SharedAllocationRecord() = delete ; + SharedAllocationRecord( const SharedAllocationRecord & ) = delete ; + SharedAllocationRecord & operator = ( const SharedAllocationRecord & ) = delete ; public: @@ -204,42 +238,48 @@ private: typedef SharedAllocationRecord Record ; - enum : unsigned long { - DO_NOT_DEREF_FLAG = 0x01ul - }; + enum : uintptr_t { DO_NOT_DEREF_FLAG = 0x01ul }; // The allocation record resides in Host memory space - Record * m_record ; - unsigned long m_record_bits; - - KOKKOS_INLINE_FUNCTION - static Record * disable( Record * rec ) - { return reinterpret_cast( reinterpret_cast( rec ) & DO_NOT_DEREF_FLAG ); } - - KOKKOS_INLINE_FUNCTION - void increment() const - { -#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) - if ( ! ( m_record_bits & DO_NOT_DEREF_FLAG ) ) Record::increment( m_record ); -#endif - } - - KOKKOS_INLINE_FUNCTION - void decrement() const - { -#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) - if ( ! ( m_record_bits & DO_NOT_DEREF_FLAG ) ) Record::decrement( m_record ); -#endif - } + Record * m_record ; + uintptr_t m_record_bits ; public: - KOKKOS_INLINE_FUNCTION - constexpr SharedAllocationTracker() : m_record_bits( DO_NOT_DEREF_FLAG ) {} + // Use macros instead of inline functions to reduce + // pressure on compiler optimization by reducing + // number of symbols and inline functons. + +#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) + +#define KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED \ + Record::tracking_enabled() + +#define KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT \ + if ( ! ( m_record_bits & DO_NOT_DEREF_FLAG ) ) Record::increment( m_record ); + +#define KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT \ + if ( ! ( m_record_bits & DO_NOT_DEREF_FLAG ) ) Record::decrement( m_record ); + +#else + +#define KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED 0 + +#define KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT /* */ + +#define KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT /* */ + +#endif + + /** \brief Assign a specialized record */ + inline + void assign_allocated_record_to_uninitialized( Record * arg_record ) + { Record::increment( m_record = arg_record ); } template< class MemorySpace > constexpr - SharedAllocationRecord< MemorySpace , void > & get_record() const + SharedAllocationRecord< MemorySpace , void > & + get_record() const { return * static_cast< SharedAllocationRecord< MemorySpace , void > * >( m_record ); } template< class MemorySpace > @@ -252,36 +292,92 @@ public: } KOKKOS_INLINE_FUNCTION - SharedAllocationTracker( Record * arg_record ) - : m_record( arg_record ) { increment(); } - - KOKKOS_INLINE_FUNCTION - ~SharedAllocationTracker() { decrement(); } - - KOKKOS_INLINE_FUNCTION - SharedAllocationTracker( const SharedAllocationTracker & rhs ) - : m_record( rhs.m_record ) { increment(); } - - KOKKOS_INLINE_FUNCTION - SharedAllocationTracker( SharedAllocationTracker && rhs ) - : m_record( rhs.m_record ) { rhs.m_record_bits = DO_NOT_DEREF_FLAG ; } - - KOKKOS_INLINE_FUNCTION - SharedAllocationTracker & operator = ( const SharedAllocationTracker & rhs ) + int use_count() const { - decrement(); - m_record = rhs.m_record ; - increment(); - return *this ; +#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) + Record * const tmp = reinterpret_cast( m_record_bits & ~DO_NOT_DEREF_FLAG ); + return ( tmp ? tmp->use_count() : 0 ); +#else + return 0 ; +#endif } - KOKKOS_INLINE_FUNCTION + KOKKOS_FORCEINLINE_FUNCTION + ~SharedAllocationTracker() + { KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT } + + KOKKOS_FORCEINLINE_FUNCTION + constexpr SharedAllocationTracker() + : m_record_bits( DO_NOT_DEREF_FLAG ) {} + + // Move: + + KOKKOS_FORCEINLINE_FUNCTION + SharedAllocationTracker( SharedAllocationTracker && rhs ) + : m_record_bits( rhs.m_record_bits ) + { rhs.m_record_bits = DO_NOT_DEREF_FLAG ; } + + KOKKOS_FORCEINLINE_FUNCTION SharedAllocationTracker & operator = ( SharedAllocationTracker && rhs ) { - m_record = rhs.m_record ; + // If this is tracking then must decrement + KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT + // Move and reset RHS to default constructed value. + m_record_bits = rhs.m_record_bits ; rhs.m_record_bits = DO_NOT_DEREF_FLAG ; return *this ; } + + // Copy: + + KOKKOS_FORCEINLINE_FUNCTION + SharedAllocationTracker( const SharedAllocationTracker & rhs ) + : m_record_bits( KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED + ? rhs.m_record_bits + : rhs.m_record_bits | DO_NOT_DEREF_FLAG ) + { + KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT + } + + /** \brief Copy construction may disable tracking. */ + KOKKOS_FORCEINLINE_FUNCTION + SharedAllocationTracker( const SharedAllocationTracker & rhs + , const bool enable_tracking ) + : m_record_bits( KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED + && enable_tracking + ? rhs.m_record_bits + : rhs.m_record_bits | DO_NOT_DEREF_FLAG ) + { KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT } + + KOKKOS_FORCEINLINE_FUNCTION + SharedAllocationTracker & operator = ( const SharedAllocationTracker & rhs ) + { + // If this is tracking then must decrement + KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT + m_record_bits = KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED + ? rhs.m_record_bits + : rhs.m_record_bits | DO_NOT_DEREF_FLAG ; + KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT + return *this ; + } + + /** \brief Copy assignment may disable tracking */ + KOKKOS_FORCEINLINE_FUNCTION + void assign( const SharedAllocationTracker & rhs + , const bool enable_tracking ) + { + KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT + m_record_bits = KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED + && enable_tracking + ? rhs.m_record_bits + : rhs.m_record_bits | DO_NOT_DEREF_FLAG ; + KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT + } + +#undef KOKKOS_SHARED_ALLOCATION_TRACKER_ENABLED +#undef KOKKOS_SHARED_ALLOCATION_TRACKER_INCREMENT +#undef KOKKOS_SHARED_ALLOCATION_TRACKER_DECREMENT + }; @@ -289,4 +385,4 @@ public: } /* namespace Experimental */ } /* namespace Kokkos */ - +#endif diff --git a/lib/kokkos/core/src/impl/KokkosExp_ViewAllocProp.hpp b/lib/kokkos/core/src/impl/KokkosExp_ViewAllocProp.hpp index 348ccaf5ed..d571a1ea0c 100644 --- a/lib/kokkos/core/src/impl/KokkosExp_ViewAllocProp.hpp +++ b/lib/kokkos/core/src/impl/KokkosExp_ViewAllocProp.hpp @@ -47,6 +47,28 @@ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + +namespace Kokkos { + +/* For backward compatibility */ + +struct ViewAllocateWithoutInitializing { + + const std::string label ; + + ViewAllocateWithoutInitializing() : label() {} + ViewAllocateWithoutInitializing( const std::string & arg_label ) : label( arg_label ) {} + ViewAllocateWithoutInitializing( const char * const arg_label ) : label( arg_label ) {} +}; + +} /* namespace Kokkos */ + +#endif + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + namespace Kokkos { namespace Experimental { namespace Impl { diff --git a/lib/kokkos/core/src/impl/KokkosExp_ViewArray.hpp b/lib/kokkos/core/src/impl/KokkosExp_ViewArray.hpp index 6f49c57b3c..432d29ab32 100644 --- a/lib/kokkos/core/src/impl/KokkosExp_ViewArray.hpp +++ b/lib/kokkos/core/src/impl/KokkosExp_ViewArray.hpp @@ -50,8 +50,8 @@ namespace Kokkos { namespace Experimental { namespace Impl { -template< class DataType , class V , long N , class P , class ArrayLayout > -struct ViewDataAnalysis< DataType , Kokkos::Array , ArrayLayout > +template< class DataType , class ArrayLayout , class V , size_t N , class P > +struct ViewDataAnalysis< DataType , ArrayLayout , Kokkos::Array > { private: @@ -73,15 +73,7 @@ private: , typename array_analysis::const_value_type >::value }; - typedef ViewDimension< ( dimension::rank == 0 ? N : dimension::arg_N0 ) - , ( dimension::rank == 1 ? N : dimension::arg_N1 ) - , ( dimension::rank == 2 ? N : dimension::arg_N2 ) - , ( dimension::rank == 3 ? N : dimension::arg_N3 ) - , ( dimension::rank == 4 ? N : dimension::arg_N4 ) - , ( dimension::rank == 5 ? N : dimension::arg_N5 ) - , ( dimension::rank == 6 ? N : dimension::arg_N6 ) - , ( dimension::rank == 7 ? N : dimension::arg_N7 ) - > array_scalar_dimension ; + typedef typename dimension::template append::type array_scalar_dimension ; typedef typename std::conditional< is_const , const V , V >::type scalar_type ; typedef V non_const_scalar_type ; @@ -113,18 +105,18 @@ namespace Impl { /** \brief View mapping for non-specialized data type and standard layout */ template< class Traits > -class ViewMapping< Traits , void , - typename std::enable_if<( std::is_same< typename Traits::specialize , Kokkos::Array<> >::value && - ( std::is_same< typename Traits::array_layout , Kokkos::LayoutLeft >::value || - std::is_same< typename Traits::array_layout , Kokkos::LayoutRight >::value || - std::is_same< typename Traits::array_layout , Kokkos::LayoutStride >::value ) - )>::type > +class ViewMapping< Traits , + typename std::enable_if<( + std::is_same< typename Traits::specialize , Kokkos::Array<> >::value && + ( std::is_same< typename Traits::array_layout , Kokkos::LayoutLeft >::value || + std::is_same< typename Traits::array_layout , Kokkos::LayoutRight >::value || + std::is_same< typename Traits::array_layout , Kokkos::LayoutStride >::value ) + )>::type > { private: - template< class , class , typename > friend class ViewMapping ; - template< class , bool , bool , bool , bool , bool , bool , bool , bool , class > friend struct SubviewMapping ; - template< class , class , class , class > friend class Kokkos::Experimental::View ; + template< class , class ... > friend class ViewMapping ; + template< class , class ... > friend class Kokkos::Experimental::View ; typedef ViewOffset< typename Traits::dimension , typename Traits::array_layout @@ -187,16 +179,20 @@ public: // Range span /** \brief Span of the mapped range */ - KOKKOS_INLINE_FUNCTION constexpr size_t span() const { return m_offset.span(); } + KOKKOS_INLINE_FUNCTION constexpr size_t span() const + { return m_offset.span() * Array_N ; } /** \brief Is the mapped range span contiguous */ - KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { return m_offset.span_is_contiguous(); } + KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const + { return m_offset.span_is_contiguous(); } typedef typename std::conditional< is_contiguous_reference , contiguous_reference , strided_reference >::type reference_type ; + typedef handle_type pointer_type ; + /** \brief If data references are lvalue_reference than can query pointer to memory */ - KOKKOS_INLINE_FUNCTION constexpr typename Traits::value_type * data() const - { return (typename Traits::value_type *) 0 ; } + KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const + { return m_handle ; } //---------------------------------------- // The View class performs all rank and bounds checking before @@ -259,14 +255,14 @@ public: private: enum { MemorySpanMask = 8 - 1 /* Force alignment on 8 byte boundary */ }; - enum { MemorySpanSize = sizeof(typename Traits::value_type) }; + enum { MemorySpanSize = sizeof(scalar_type) }; public: /** \brief Span, in bytes, of the referenced memory */ KOKKOS_INLINE_FUNCTION constexpr size_t memory_span() const { - return ( m_stride * sizeof(typename Traits::value_type) + MemorySpanMask ) & ~size_t(MemorySpanMask); + return ( m_offset.span() * Array_N * MemorySpanSize + MemorySpanMask ) & ~size_t(MemorySpanMask); } /** \brief Span, in bytes, of the required memory */ @@ -277,7 +273,7 @@ public: , const size_t N4 , const size_t N5 , const size_t N6 , const size_t N7 ) { typedef std::integral_constant< unsigned , AllowPadding ? MemorySpanSize : 0 > padding ; - return ( offset_type( padding(), N0, N1, N2, N3, N4, N5, N6, N7 ).span() * MemorySpanSize + MemorySpanMask ) & ~size_t(MemorySpanMask); + return ( offset_type( padding(), N0, N1, N2, N3, N4, N5, N6, N7 ).span() * Array_N * MemorySpanSize + MemorySpanMask ) & ~size_t(MemorySpanMask); } /** \brief Span, in bytes, of the required memory */ @@ -286,7 +282,7 @@ public: static constexpr size_t memory_span( const std::integral_constant & , const typename Traits::array_layout & layout ) { - return ( offset_type( layout ).span() * MemorySpanSize + MemorySpanMask ) & ~size_t(MemorySpanMask); + return ( offset_type( layout ).span() * Array_N * MemorySpanSize + MemorySpanMask ) & ~size_t(MemorySpanMask); } //---------------------------------------- @@ -305,11 +301,11 @@ public: template< bool AllowPadding > KOKKOS_INLINE_FUNCTION - ViewMapping( void * ptr + ViewMapping( pointer_type ptr , const std::integral_constant & , const size_t N0 , const size_t N1 , const size_t N2 , const size_t N3 , const size_t N4 , const size_t N5 , const size_t N6 , const size_t N7 ) - : m_handle( reinterpret_cast< handle_type >( ptr ) ) + : m_handle( ptr ) , m_offset( std::integral_constant< unsigned , AllowPadding ? sizeof(typename Traits::value_type) : 0 >() , N0, N1, N2, N3, N4, N5, N6, N7 ) , m_stride( m_offset.span() ) @@ -317,10 +313,10 @@ public: template< bool AllowPadding > KOKKOS_INLINE_FUNCTION - ViewMapping( void * ptr + ViewMapping( pointer_type ptr , const std::integral_constant & , const typename Traits::array_layout & layout ) - : m_handle( reinterpret_cast< handle_type >( ptr ) ) + : m_handle( ptr ) , m_offset( layout ) , m_stride( m_offset.span() ) {} @@ -340,7 +336,8 @@ public: { typedef Kokkos::RangePolicy< ExecSpace , size_t > Policy ; - (void) Kokkos::Impl::ParallelFor< ViewMapping , Policy >( *this , Policy( 0 , m_stride ) ); + const Kokkos::Impl::ParallelFor< ViewMapping , Policy > closure( *this , Policy( 0 , m_stride ) ); + closure.execute(); ExecSpace::fence(); } @@ -379,8 +376,8 @@ public: enum { is_assignable = true }; typedef Kokkos::Experimental::Impl::SharedAllocationTracker TrackType ; - typedef ViewMapping< DstTraits , void , void > DstType ; - typedef ViewMapping< SrcTraits , void , void > SrcType ; + typedef ViewMapping< DstTraits , void > DstType ; + typedef ViewMapping< SrcTraits , void > SrcType ; KOKKOS_INLINE_FUNCTION static void assign( DstType & dst , const SrcType & src , const TrackType & src_track ) @@ -438,8 +435,8 @@ public: std::is_same< typename DstTraits::array_layout , typename SrcTraits::array_layout >::value }; typedef Kokkos::Experimental::Impl::SharedAllocationTracker TrackType ; - typedef ViewMapping< DstTraits , void , void > DstType ; - typedef ViewMapping< SrcTraits , void , void > SrcType ; + typedef ViewMapping< DstTraits , void > DstType ; + typedef ViewMapping< SrcTraits , void > SrcType ; KOKKOS_INLINE_FUNCTION static void assign( DstType & dst , const SrcType & src , const TrackType & src_track ) @@ -452,6 +449,7 @@ public: // Arguments beyond the destination rank are ignored. if ( src.span_is_contiguous() ) { // not padded dst.m_offset = dst_offset_type( std::integral_constant() + , ( 0 < SrcType::Rank ? src.dimension_0() : SrcTraits::value_type::size() ) , ( 1 < SrcType::Rank ? src.dimension_1() : SrcTraits::value_type::size() ) , ( 2 < SrcType::Rank ? src.dimension_2() : SrcTraits::value_type::size() ) , ( 3 < SrcType::Rank ? src.dimension_3() : SrcTraits::value_type::size() ) @@ -483,34 +481,47 @@ public: //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -/** \brief View mapping for non-specialized data type and standard layout */ -template< class Traits , bool R0 , bool R1 , bool R2 , bool R3 , bool R4 , bool R5 , bool R6 , bool R7 > -struct SubviewMapping< Traits, R0, R1, R2, R3, R4, R5, R6, R7 , - typename std::enable_if<( - std::is_same< typename Traits::specialize , Kokkos::Array<> >::value - && - ( - std::is_same< typename Traits::array_layout , Kokkos::LayoutLeft >::value || - std::is_same< typename Traits::array_layout , Kokkos::LayoutRight >::value || - std::is_same< typename Traits::array_layout , Kokkos::LayoutStride >::value - ) - )>::type > +template< class SrcTraits , class ... Args > +struct ViewMapping + < typename std::enable_if<( + std::is_same< typename SrcTraits::specialize , Kokkos::Array<> >::value + && + ( + std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutLeft >::value || + std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutRight >::value || + std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutStride >::value + ) + )>::type + , SrcTraits + , Args ... > { private: - // Subview's rank + static_assert( SrcTraits::rank == sizeof...(Args) , "" ); + + enum : bool + { R0 = is_integral_extent<0,Args...>::value + , R1 = is_integral_extent<1,Args...>::value + , R2 = is_integral_extent<2,Args...>::value + , R3 = is_integral_extent<3,Args...>::value + , R4 = is_integral_extent<4,Args...>::value + , R5 = is_integral_extent<5,Args...>::value + , R6 = is_integral_extent<6,Args...>::value + , R7 = is_integral_extent<7,Args...>::value + }; + enum { rank = unsigned(R0) + unsigned(R1) + unsigned(R2) + unsigned(R3) + unsigned(R4) + unsigned(R5) + unsigned(R6) + unsigned(R7) }; // Whether right-most rank is a range. - enum { R0_rev = 0 == Traits::rank ? false : ( - 1 == Traits::rank ? R0 : ( - 2 == Traits::rank ? R1 : ( - 3 == Traits::rank ? R2 : ( - 4 == Traits::rank ? R3 : ( - 5 == Traits::rank ? R4 : ( - 6 == Traits::rank ? R5 : ( - 7 == Traits::rank ? R6 : R7 ))))))) }; + enum { R0_rev = 0 == SrcTraits::rank ? false : ( + 1 == SrcTraits::rank ? R0 : ( + 2 == SrcTraits::rank ? R1 : ( + 3 == SrcTraits::rank ? R2 : ( + 4 == SrcTraits::rank ? R3 : ( + 5 == SrcTraits::rank ? R4 : ( + 6 == SrcTraits::rank ? R5 : ( + 7 == SrcTraits::rank ? R6 : R7 ))))))) }; // Subview's layout typedef typename std::conditional< @@ -519,15 +530,15 @@ private: || // OutputRank 1 or 2, InputLayout Left, Interval 0 // because single stride one or second index has a stride. - ( rank <= 2 && R0 && std::is_same< typename Traits::array_layout , Kokkos::LayoutLeft >::value ) + ( rank <= 2 && R0 && std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutLeft >::value ) || // OutputRank 1 or 2, InputLayout Right, Interval [InputRank-1] // because single stride one or second index has a stride. - ( rank <= 2 && R0_rev && std::is_same< typename Traits::array_layout , Kokkos::LayoutRight >::value ) - ), typename Traits::array_layout , Kokkos::LayoutStride + ( rank <= 2 && R0_rev && std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutRight >::value ) + ), typename SrcTraits::array_layout , Kokkos::LayoutStride >::type array_layout ; - typedef typename Traits::value_type value_type ; + typedef typename SrcTraits::value_type value_type ; typedef typename std::conditional< rank == 0 , value_type , typename std::conditional< rank == 1 , value_type * , @@ -543,66 +554,41 @@ private: public: - typedef - Kokkos::Experimental::ViewTraits< data_type , array_layout - , typename Traits::device_type - , typename Traits::memory_traits > traits_type ; + typedef Kokkos::Experimental::ViewTraits + < data_type + , array_layout + , typename SrcTraits::device_type + , typename SrcTraits::memory_traits > traits_type ; - typedef Kokkos::Experimental::View< data_type - , array_layout - , typename Traits::device_type - , typename Traits::memory_traits > type ; + typedef Kokkos::Experimental::View + < data_type + , array_layout + , typename SrcTraits::device_type + , typename SrcTraits::memory_traits > type ; - template< class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 , class T6 , class T7 > KOKKOS_INLINE_FUNCTION - static void assign( ViewMapping< traits_type , void , void > & dst - , ViewMapping< Traits , void , void > const & src - , T0 const & arg0 - , T1 const & arg1 - , T2 const & arg2 - , T3 const & arg3 - , T4 const & arg4 - , T5 const & arg5 - , T6 const & arg6 - , T7 const & arg7 - ) + static void assign( ViewMapping< traits_type , void > & dst + , ViewMapping< SrcTraits , void > const & src + , Args ... args ) { - typedef ViewMapping< traits_type , void , void > DstType ; + typedef ViewMapping< traits_type , void > DstType ; typedef typename DstType::offset_type dst_offset_type ; typedef typename DstType::handle_type dst_handle_type ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V0 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V1 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V2 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V3 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V4 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V5 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V6 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V7 ; - - dst.m_offset = dst_offset_type - ( src.m_offset - , V0::dimension( src.m_offset.dimension_0() , arg0 ) - , V1::dimension( src.m_offset.dimension_1() , arg1 ) - , V2::dimension( src.m_offset.dimension_2() , arg2 ) - , V3::dimension( src.m_offset.dimension_3() , arg3 ) - , V4::dimension( src.m_offset.dimension_4() , arg4 ) - , V5::dimension( src.m_offset.dimension_5() , arg5 ) - , V6::dimension( src.m_offset.dimension_6() , arg6 ) - , V7::dimension( src.m_offset.dimension_7() , arg7 ) - ); + const SubviewExtents< SrcTraits::rank , rank > + extents( src.m_offset.m_dim , args... ); + dst.m_offset = dst_offset_type( src.m_offset , extents ); dst.m_handle = dst_handle_type( src.m_handle + - src.m_offset( V0::begin( arg0 ) - , V1::begin( arg1 ) - , V2::begin( arg2 ) - , V3::begin( arg3 ) - , V4::begin( arg4 ) - , V5::begin( arg5 ) - , V6::begin( arg6 ) - , V7::begin( arg7 ) + src.m_offset( extents.domain_offset(0) + , extents.domain_offset(1) + , extents.domain_offset(2) + , extents.domain_offset(3) + , extents.domain_offset(4) + , extents.domain_offset(5) + , extents.domain_offset(6) + , extents.domain_offset(7) ) ); } }; diff --git a/lib/kokkos/core/src/impl/KokkosExp_ViewMapping.hpp b/lib/kokkos/core/src/impl/KokkosExp_ViewMapping.hpp index 5fa1bb7155..5ec003222f 100644 --- a/lib/kokkos/core/src/impl/KokkosExp_ViewMapping.hpp +++ b/lib/kokkos/core/src/impl/KokkosExp_ViewMapping.hpp @@ -49,6 +49,7 @@ #include #include +#include #include #include @@ -69,372 +70,162 @@ namespace Kokkos { namespace Experimental { namespace Impl { -template< long sN0 = -1 - , long sN1 = -1 - , long sN2 = -1 - , long sN3 = -1 - , long sN4 = -1 - , long sN5 = -1 - , long sN6 = -1 - , long sN7 = -1 - > -struct ViewDimension { +template< unsigned I , size_t ... Args > +struct variadic_size_t + { enum { value = ~size_t(0) }; }; - enum { arg_N0 = sN0 }; - enum { arg_N1 = sN1 }; - enum { arg_N2 = sN2 }; - enum { arg_N3 = sN3 }; - enum { arg_N4 = sN4 }; - enum { arg_N5 = sN5 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; +template< size_t Val , size_t ... Args > +struct variadic_size_t< 0 , Val , Args ... > + { enum { value = Val }; }; - enum { rank = ( sN0 < 0 ? 0 : - ( sN1 < 0 ? 1 : - ( sN2 < 0 ? 2 : - ( sN3 < 0 ? 3 : - ( sN4 < 0 ? 4 : - ( sN5 < 0 ? 5 : - ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 )))))))) }; - enum { rank_dynamic = 0 }; +template< unsigned I , size_t Val , size_t ... Args > +struct variadic_size_t< I , Val , Args ... > + { enum { value = variadic_size_t< I - 1 , Args ... >::value }; }; - enum { N0 = 0 < sN0 ? sN0 : 1 }; - enum { N1 = 0 < sN1 ? sN1 : 1 }; - enum { N2 = 0 < sN2 ? sN2 : 1 }; - enum { N3 = 0 < sN3 ? sN3 : 1 }; - enum { N4 = 0 < sN4 ? sN4 : 1 }; - enum { N5 = 0 < sN5 ? sN5 : 1 }; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t , unsigned , unsigned , unsigned - , unsigned , unsigned , unsigned , unsigned ) {} -}; - -template< long sN1 - , long sN2 - , long sN3 - , long sN4 - , long sN5 - , long sN6 - , long sN7 - > -struct ViewDimension< 0, sN1, sN2, sN3, sN4, sN5, sN6, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = sN1 }; - enum { arg_N2 = sN2 }; - enum { arg_N3 = sN3 }; - enum { arg_N4 = sN4 }; - enum { arg_N5 = sN5 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN1 < 0 ? 1 : - ( sN2 < 0 ? 2 : - ( sN3 < 0 ? 3 : - ( sN4 < 0 ? 4 : - ( sN5 < 0 ? 5 : - ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 ))))))) }; - enum { rank_dynamic = 1 }; - - size_t N0 ; /* When 1 == rank_dynamic allow N0 >= 2^32 */ - enum { N1 = 0 < sN1 ? sN1 : 1 }; - enum { N2 = 0 < sN2 ? sN2 : 1 }; - enum { N3 = 0 < sN3 ? sN3 : 1 }; - enum { N4 = 0 < sN4 ? sN4 : 1 }; - enum { N5 = 0 < sN5 ? sN5 : 1 }; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned , unsigned , unsigned - , unsigned , unsigned , unsigned , unsigned ) - : N0( aN0 ) {} -}; - -template< long sN2 - , long sN3 - , long sN4 - , long sN5 - , long sN6 - , long sN7 - > -struct ViewDimension< 0, 0, sN2, sN3, sN4, sN5, sN6, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = sN2 }; - enum { arg_N3 = sN3 }; - enum { arg_N4 = sN4 }; - enum { arg_N5 = sN5 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN2 < 0 ? 2 : - ( sN3 < 0 ? 3 : - ( sN4 < 0 ? 4 : - ( sN5 < 0 ? 5 : - ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 )))))) }; - enum { rank_dynamic = 2 }; - - size_t N0 ; /* When 2 == rank_dynamic allow N0 >= 2^32 */ - size_t N1 ; /* When 2 == rank_dynamic allow N1 >= 2^32 */ - enum { N2 = 0 < sN2 ? sN2 : 1 }; - enum { N3 = 0 < sN3 ? sN3 : 1 }; - enum { N4 = 0 < sN4 ? sN4 : 1 }; - enum { N5 = 0 < sN5 ? sN5 : 1 }; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned , unsigned - , unsigned , unsigned , unsigned , unsigned ) - : N0( aN0 ) , N1( aN1 ) {} -}; - -template< long sN3 - , long sN4 - , long sN5 - , long sN6 - , long sN7 - > -struct ViewDimension< 0, 0, 0, sN3, sN4, sN5, sN6, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = 0 }; - enum { arg_N3 = sN3 }; - enum { arg_N4 = sN4 }; - enum { arg_N5 = sN5 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN3 < 0 ? 3 : - ( sN4 < 0 ? 4 : - ( sN5 < 0 ? 5 : - ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 ))))) }; - enum { rank_dynamic = 3 }; - - unsigned N0 ; - unsigned N1 ; - unsigned N2 ; - enum { N3 = 0 < sN3 ? sN3 : 1 }; - enum { N4 = 0 < sN4 ? sN4 : 1 }; - enum { N5 = 0 < sN5 ? sN5 : 1 }; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned aN2 , unsigned - , unsigned , unsigned , unsigned , unsigned ) - : N0( aN0 ) , N1( aN1 ) , N2( aN2 ) {} -}; - -template< long sN4 - , long sN5 - , long sN6 - , long sN7 - > -struct ViewDimension< 0, 0, 0, 0, sN4, sN5, sN6, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = 0 }; - enum { arg_N3 = 0 }; - enum { arg_N4 = sN4 }; - enum { arg_N5 = sN5 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN4 < 0 ? 4 : - ( sN5 < 0 ? 5 : - ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 )))) }; - enum { rank_dynamic = 4 }; - - unsigned N0 ; - unsigned N1 ; - unsigned N2 ; - unsigned N3 ; - enum { N4 = 0 < sN4 ? sN4 : 1 }; - enum { N5 = 0 < sN5 ? sN5 : 1 }; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned aN2 , unsigned aN3 - , unsigned , unsigned , unsigned , unsigned ) - : N0( aN0 ) , N1( aN1 ) , N2( aN2 ) , N3( aN3 ) {} -}; - -template< long sN5 - , long sN6 - , long sN7 - > -struct ViewDimension< 0, 0, 0, 0, 0, sN5, sN6, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = 0 }; - enum { arg_N3 = 0 }; - enum { arg_N4 = 0 }; - enum { arg_N5 = sN5 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN5 < 0 ? 5 : - ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 ))) }; - enum { rank_dynamic = 5 }; - - unsigned N0 ; - unsigned N1 ; - unsigned N2 ; - unsigned N3 ; - unsigned N4 ; - enum { N5 = 0 < sN5 ? sN5 : 1 }; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned aN2 , unsigned aN3 - , unsigned aN4 , unsigned , unsigned , unsigned ) - : N0( aN0 ) , N1( aN1 ) , N2( aN2 ) , N3( aN3 ) , N4( aN4 ) {} -}; - -template< long sN6 - , long sN7 - > -struct ViewDimension< 0, 0, 0, 0, 0, 0, sN6, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = 0 }; - enum { arg_N3 = 0 }; - enum { arg_N4 = 0 }; - enum { arg_N5 = 0 }; - enum { arg_N6 = sN6 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN6 < 0 ? 6 : - ( sN7 < 0 ? 7 : 8 )) }; - enum { rank_dynamic = 6 }; - - unsigned N0 ; - unsigned N1 ; - unsigned N2 ; - unsigned N3 ; - unsigned N4 ; - unsigned N5 ; - enum { N6 = 0 < sN6 ? sN6 : 1 }; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned aN2 , unsigned aN3 - , unsigned aN4 , unsigned aN5 , unsigned , unsigned ) - : N0( aN0 ) , N1( aN1 ) , N2( aN2 ) , N3( aN3 ) , N4( aN4 ) , N5( aN5 ) {} -}; - -template< long sN7 > -struct ViewDimension< 0, 0, 0, 0, 0, 0, 0, sN7 > { - - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = 0 }; - enum { arg_N3 = 0 }; - enum { arg_N4 = 0 }; - enum { arg_N5 = 0 }; - enum { arg_N6 = 0 }; - enum { arg_N7 = sN7 }; - - enum { rank = ( sN7 < 0 ? 7 : 8 ) }; - enum { rank_dynamic = 7 }; - - unsigned N0 ; - unsigned N1 ; - unsigned N2 ; - unsigned N3 ; - unsigned N4 ; - unsigned N5 ; - unsigned N6 ; - enum { N7 = 0 < sN7 ? sN7 : 1 }; - - ViewDimension() = default ; - ViewDimension( const ViewDimension & ) = default ; - ViewDimension & operator = ( const ViewDimension & ) = default ; - - KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned aN2 , unsigned aN3 - , unsigned aN4 , unsigned aN5 , unsigned aN6 , unsigned ) - : N0( aN0 ) , N1( aN1 ) , N2( aN2 ) , N3( aN3 ) , N4( aN4 ) , N5( aN5 ) , N6( aN6 ) {} -}; +template< size_t ... Args > +struct rank_dynamic ; template<> -struct ViewDimension< 0, 0, 0, 0, 0, 0, 0, 0 > { +struct rank_dynamic<> { enum { value = 0 }; }; - enum { arg_N0 = 0 }; - enum { arg_N1 = 0 }; - enum { arg_N2 = 0 }; - enum { arg_N3 = 0 }; - enum { arg_N4 = 0 }; - enum { arg_N5 = 0 }; - enum { arg_N6 = 0 }; - enum { arg_N7 = 0 }; +template< size_t Val , size_t ... Args > +struct rank_dynamic< Val , Args... > +{ + enum { value = ( Val == 0 ? 1 : 0 ) + rank_dynamic< Args... >::value }; +}; - enum { rank = 8 }; - enum { rank_dynamic = 8 }; +#define KOKKOS_IMPL_VIEW_DIMENSION( R ) \ + template< size_t V , unsigned > struct ViewDimension ## R \ + { \ + enum { ArgN ## R = ( V != ~size_t(0) ? V : 1 ) }; \ + enum { N ## R = ( V != ~size_t(0) ? V : 1 ) }; \ + KOKKOS_INLINE_FUNCTION explicit ViewDimension ## R ( size_t ) {} \ + ViewDimension ## R () = default ; \ + ViewDimension ## R ( const ViewDimension ## R & ) = default ; \ + ViewDimension ## R & operator = ( const ViewDimension ## R & ) = default ; \ + }; \ + template< unsigned RD > struct ViewDimension ## R < 0 , RD > \ + { \ + enum { ArgN ## R = 0 }; \ + typename std::conditional<( RD < 3 ), size_t , unsigned >::type N ## R ; \ + ViewDimension ## R () = default ; \ + ViewDimension ## R ( const ViewDimension ## R & ) = default ; \ + ViewDimension ## R & operator = ( const ViewDimension ## R & ) = default ; \ + KOKKOS_INLINE_FUNCTION explicit ViewDimension ## R ( size_t V ) : N ## R ( V ) {} \ + }; - unsigned N0 ; - unsigned N1 ; - unsigned N2 ; - unsigned N3 ; - unsigned N4 ; - unsigned N5 ; - unsigned N6 ; - unsigned N7 ; +KOKKOS_IMPL_VIEW_DIMENSION( 0 ) +KOKKOS_IMPL_VIEW_DIMENSION( 1 ) +KOKKOS_IMPL_VIEW_DIMENSION( 2 ) +KOKKOS_IMPL_VIEW_DIMENSION( 3 ) +KOKKOS_IMPL_VIEW_DIMENSION( 4 ) +KOKKOS_IMPL_VIEW_DIMENSION( 5 ) +KOKKOS_IMPL_VIEW_DIMENSION( 6 ) +KOKKOS_IMPL_VIEW_DIMENSION( 7 ) + +#undef KOKKOS_IMPL_VIEW_DIMENSION + +template< size_t ... Vals > +struct ViewDimension + : public ViewDimension0< variadic_size_t<0,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension1< variadic_size_t<1,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension2< variadic_size_t<2,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension3< variadic_size_t<3,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension4< variadic_size_t<4,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension5< variadic_size_t<5,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension6< variadic_size_t<6,Vals...>::value + , rank_dynamic< Vals... >::value > + , public ViewDimension7< variadic_size_t<7,Vals...>::value + , rank_dynamic< Vals... >::value > +{ + typedef ViewDimension0< variadic_size_t<0,Vals...>::value + , rank_dynamic< Vals... >::value > D0 ; + typedef ViewDimension1< variadic_size_t<1,Vals...>::value + , rank_dynamic< Vals... >::value > D1 ; + typedef ViewDimension2< variadic_size_t<2,Vals...>::value + , rank_dynamic< Vals... >::value > D2 ; + typedef ViewDimension3< variadic_size_t<3,Vals...>::value + , rank_dynamic< Vals... >::value > D3 ; + typedef ViewDimension4< variadic_size_t<4,Vals...>::value + , rank_dynamic< Vals... >::value > D4 ; + typedef ViewDimension5< variadic_size_t<5,Vals...>::value + , rank_dynamic< Vals... >::value > D5 ; + typedef ViewDimension6< variadic_size_t<6,Vals...>::value + , rank_dynamic< Vals... >::value > D6 ; + typedef ViewDimension7< variadic_size_t<7,Vals...>::value + , rank_dynamic< Vals... >::value > D7 ; + + using D0::ArgN0 ; + using D1::ArgN1 ; + using D2::ArgN2 ; + using D3::ArgN3 ; + using D4::ArgN4 ; + using D5::ArgN5 ; + using D6::ArgN6 ; + using D7::ArgN7 ; + + using D0::N0 ; + using D1::N1 ; + using D2::N2 ; + using D3::N3 ; + using D4::N4 ; + using D5::N5 ; + using D6::N6 ; + using D7::N7 ; + + enum { rank = sizeof...(Vals) }; + enum { rank_dynamic = Impl::rank_dynamic< Vals... >::value }; ViewDimension() = default ; ViewDimension( const ViewDimension & ) = default ; ViewDimension & operator = ( const ViewDimension & ) = default ; KOKKOS_INLINE_FUNCTION - constexpr ViewDimension( size_t aN0 , unsigned aN1 , unsigned aN2 , unsigned aN3 - , unsigned aN4 , unsigned aN5 , unsigned aN6 , unsigned aN7 ) - : N0( aN0 ) , N1( aN1 ) , N2( aN2 ) , N3( aN3 ) , N4( aN4 ) , N5( aN5 ) , N6( aN6 ) , N7( aN7 ) {} + constexpr + ViewDimension( size_t n0 , size_t n1 , size_t n2 , size_t n3 + , size_t n4 , size_t n5 , size_t n6 , size_t n7 ) + : D0( n0 ) + , D1( n1 ) + , D2( n2 ) + , D3( n3 ) + , D4( n4 ) + , D5( n5 ) + , D6( n6 ) + , D7( n7 ) + {} + + KOKKOS_INLINE_FUNCTION + constexpr size_t extent( const unsigned r ) const + { + return r == 0 ? N0 : ( + r == 1 ? N1 : ( + r == 2 ? N2 : ( + r == 3 ? N3 : ( + r == 4 ? N4 : ( + r == 5 ? N5 : ( + r == 6 ? N6 : ( + r == 7 ? N7 : 0 ))))))); + } + + template< size_t N > + struct prepend { typedef ViewDimension< N , Vals... > type ; }; + + template< size_t N > + struct append { typedef ViewDimension< Vals... , N > type ; }; +}; + +template< class A , class B > +struct ViewDimensionJoin ; + +template< size_t ... A , size_t ... B > +struct ViewDimensionJoin< ViewDimension< A... > , ViewDimension< B... > > { + typedef ViewDimension< A... , B... > type ; }; //---------------------------------------------------------------------------- @@ -442,24 +233,381 @@ struct ViewDimension< 0, 0, 0, 0, 0, 0, 0, 0 > { template< class DstDim , class SrcDim > struct ViewDimensionAssignable ; -template< long dN0 , long dN1 , long dN2 , long dN3 , long dN4 , long dN5 , long dN6 , long dN7 - , long sN0 , long sN1 , long sN2 , long sN3 , long sN4 , long sN5 , long sN6 , long sN7 > -struct ViewDimensionAssignable< ViewDimension - , ViewDimension > +template< size_t ... DstArgs , size_t ... SrcArgs > +struct ViewDimensionAssignable< ViewDimension< DstArgs ... > + , ViewDimension< SrcArgs ... > > { - typedef ViewDimension dst ; - typedef ViewDimension src ; + typedef ViewDimension< DstArgs... > dst ; + typedef ViewDimension< SrcArgs... > src ; - enum { value = dst::rank == src::rank && - dst::rank_dynamic >= src::rank_dynamic && - ( 0 < dst::rank_dynamic || dN0 == sN0 ) && - ( 1 < dst::rank_dynamic || dN1 == sN1 ) && - ( 2 < dst::rank_dynamic || dN2 == sN2 ) && - ( 3 < dst::rank_dynamic || dN3 == sN3 ) && - ( 4 < dst::rank_dynamic || dN4 == sN4 ) && - ( 5 < dst::rank_dynamic || dN5 == sN5 ) && - ( 6 < dst::rank_dynamic || dN6 == sN6 ) && - ( 7 < dst::rank_dynamic || dN7 == sN7 ) }; + enum { value = + dst::rank == src::rank && + dst::rank_dynamic >= src::rank_dynamic && + ( 0 < dst::rank_dynamic || size_t(dst::ArgN0) == size_t(src::ArgN0) ) && + ( 1 < dst::rank_dynamic || size_t(dst::ArgN1) == size_t(src::ArgN1) ) && + ( 2 < dst::rank_dynamic || size_t(dst::ArgN2) == size_t(src::ArgN2) ) && + ( 3 < dst::rank_dynamic || size_t(dst::ArgN3) == size_t(src::ArgN3) ) && + ( 4 < dst::rank_dynamic || size_t(dst::ArgN4) == size_t(src::ArgN4) ) && + ( 5 < dst::rank_dynamic || size_t(dst::ArgN5) == size_t(src::ArgN5) ) && + ( 6 < dst::rank_dynamic || size_t(dst::ArgN6) == size_t(src::ArgN6) ) && + ( 7 < dst::rank_dynamic || size_t(dst::ArgN7) == size_t(src::ArgN7) ) }; +}; + +}}} // namespace Kokkos::Experimental::Impl + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Experimental { +namespace Impl { + +struct ALL_t { + KOKKOS_INLINE_FUNCTION + constexpr const ALL_t & operator()() const { return *this ; } +}; + +template< class T > +struct is_integral_extent_type +{ enum { value = std::is_same::value ? 1 : 0 }; }; + +template< class iType > +struct is_integral_extent_type< std::pair > +{ enum { value = std::is_integral::value ? 1 : 0 }; }; + +template< class iType > +struct is_integral_extent_type< Kokkos::pair > +{ enum { value = std::is_integral::value ? 1 : 0 }; }; + +// Assuming '2 == initializer_list::size()' +template< class iType > +struct is_integral_extent_type< std::initializer_list > +{ enum { value = std::is_integral::value ? 1 : 0 }; }; + +template < unsigned I , class ... Args > +struct is_integral_extent +{ + // variadic_type is void when sizeof...(Args) <= I + typedef typename std::remove_cv< + typename std::remove_reference< + typename Kokkos::Impl::variadic_type::type >::type >::type type ; + + enum { value = is_integral_extent_type::value }; + + static_assert( value || + std::is_integral::value || + std::is_same::value + , "subview argument must be either integral or integral extent" ); +}; + +template< unsigned DomainRank , unsigned RangeRank > +struct SubviewExtents { +private: + + // Cannot declare zero-length arrays + enum { InternalRangeRank = RangeRank ? RangeRank : 1u }; + + size_t m_begin[ DomainRank ]; + size_t m_length[ InternalRangeRank ]; + unsigned m_index[ InternalRangeRank ]; + + template< size_t ... DimArgs > + KOKKOS_FORCEINLINE_FUNCTION + bool set( unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim ) + { return true ; } + + template< class T , size_t ... DimArgs , class ... Args > + KOKKOS_FORCEINLINE_FUNCTION + bool set( unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const T & val + , Args ... args ) + { + const size_t v = static_cast(val); + + m_begin[ domain_rank ] = v ; + + return set( domain_rank + 1 , range_rank , dim , args... ) +#if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) + && ( v < dim.extent( domain_rank ) ) +#endif + ; + } + + // std::pair range + template< size_t ... DimArgs , class ... Args > + KOKKOS_FORCEINLINE_FUNCTION + bool set( unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const Kokkos::Experimental::Impl::ALL_t + , Args ... args ) + { + m_begin[ domain_rank ] = 0 ; + m_length[ range_rank ] = dim.extent( domain_rank ); + m_index[ range_rank ] = domain_rank ; + + return set( domain_rank + 1 , range_rank + 1 , dim , args... ); + } + + // std::pair range + template< class T , size_t ... DimArgs , class ... Args > + KOKKOS_FORCEINLINE_FUNCTION + bool set( unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const std::pair & val + , Args ... args ) + { + const size_t b = static_cast( val.first ); + const size_t e = static_cast( val.second ); + + m_begin[ domain_rank ] = b ; + m_length[ range_rank ] = e - b ; + m_index[ range_rank ] = domain_rank ; + + return set( domain_rank + 1 , range_rank + 1 , dim , args... ) +#if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) + && ( e <= b + dim.extent( domain_rank ) ) +#endif + ; + } + + // Kokkos::pair range + template< class T , size_t ... DimArgs , class ... Args > + KOKKOS_FORCEINLINE_FUNCTION + bool set( unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const Kokkos::pair & val + , Args ... args ) + { + const size_t b = static_cast( val.first ); + const size_t e = static_cast( val.second ); + + m_begin[ domain_rank ] = b ; + m_length[ range_rank ] = e - b ; + m_index[ range_rank ] = domain_rank ; + + return set( domain_rank + 1 , range_rank + 1 , dim , args... ) +#if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) + && ( e <= b + dim.extent( domain_rank ) ) +#endif + ; + } + + // { begin , end } range + template< class T , size_t ... DimArgs , class ... Args > + KOKKOS_FORCEINLINE_FUNCTION + bool set( unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const std::initializer_list< T > & val + , Args ... args ) + { + const size_t b = static_cast( val.begin()[0] ); + const size_t e = static_cast( val.begin()[1] ); + + m_begin[ domain_rank ] = b ; + m_length[ range_rank ] = e - b ; + m_index[ range_rank ] = domain_rank ; + + return set( domain_rank + 1 , range_rank + 1 , dim , args... ) +#if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) + && ( val.size() == 2 ) + && ( e <= b + dim.extent( domain_rank ) ) +#endif + ; + } + + //------------------------------ + +#if defined( KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK ) + + template< size_t ... DimArgs > + void error( char * + , int + , unsigned + , unsigned + , const ViewDimension< DimArgs ... > & ) const + {} + + template< class T , size_t ... DimArgs , class ... Args > + void error( char * buf , int buf_len + , unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const T & val + , Args ... args ) const + { + const int n = std::min( buf_len , + snprintf( buf , buf_len + , " %lu < %lu %c" + , static_cast(val) + , static_cast( dim.extent( domain_rank ) ) + , int( sizeof...(Args) ? ',' : ')' ) ) ); + + error( buf+n, buf_len-n, domain_rank + 1 , range_rank , dim , args... ); + } + + // std::pair range + template< size_t ... DimArgs , class ... Args > + void error( char * buf , int buf_len + , unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const Kokkos::Experimental::Impl::ALL_t + , Args ... args ) const + { + const int n = std::min( buf_len , + snprintf( buf , buf_len + , " Kokkos::ALL %c" + , int( sizeof...(Args) ? ',' : ')' ) ) ); + + error( buf+n , buf_len-n , domain_rank + 1 , range_rank + 1 , dim , args... ); + } + + // std::pair range + template< class T , size_t ... DimArgs , class ... Args > + void error( char * buf , int buf_len + , unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const std::pair & val + , Args ... args ) const + { + // d <= e - b + const int n = std::min( buf_len , + snprintf( buf , buf_len + , " %lu <= %lu - %lu %c" + , static_cast( dim.extent( domain_rank ) ) + , static_cast( val.second ) + , static_cast( val.begin ) + , int( sizeof...(Args) ? ',' : ')' ) ) ); + + error( buf+n , buf_len-n , domain_rank + 1 , range_rank + 1 , dim , args... ); + } + + // Kokkos::pair range + template< class T , size_t ... DimArgs , class ... Args > + void error( char * buf , int buf_len + , unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const Kokkos::pair & val + , Args ... args ) const + { + // d <= e - b + const int n = std::min( buf_len , + snprintf( buf , buf_len + , " %lu <= %lu - %lu %c" + , static_cast( dim.extent( domain_rank ) ) + , static_cast( val.second ) + , static_cast( val.begin ) + , int( sizeof...(Args) ? ',' : ')' ) ) ); + + error( buf+n , buf_len-n , domain_rank + 1 , range_rank + 1 , dim , args... ); + } + + // { begin , end } range + template< class T , size_t ... DimArgs , class ... Args > + void error( char * buf , int buf_len + , unsigned domain_rank + , unsigned range_rank + , const ViewDimension< DimArgs ... > & dim + , const std::initializer_list< T > & val + , Args ... args ) const + { + // d <= e - b + int n = 0 ; + if ( val.size() == 2 ) { + n = std::min( buf_len , + snprintf( buf , buf_len + , " %lu <= %lu - %lu %c" + , static_cast( dim.extent( domain_rank ) ) + , static_cast( val.begin()[0] ) + , static_cast( val.begin()[1] ) + , int( sizeof...(Args) ? ',' : ')' ) ) ); + } + else { + n = std::min( buf_len , + snprintf( buf , buf_len + , " { ... }.size() == %u %c" + , unsigned(val.size()) + , int( sizeof...(Args) ? ',' : ')' ) ) ); + } + + error( buf+n , buf_len-n , domain_rank + 1 , range_rank + 1 , dim , args... ); + } + + template< size_t ... DimArgs , class ... Args > + void error( const ViewDimension< DimArgs ... > & dim , Args ... args ) const + { +#if defined( KOKKOS_ACTIVE_EXECUTION_SPACE_HOST ) + enum { LEN = 1024 }; + char buffer[ LEN ]; + + const int n = snprintf(buffer,LEN,"Kokkos::subview bounds error ("); + error( buffer+n , LEN-n , 0 , 0 , dim , args... ); + + Kokkos::Impl::throw_runtime_exception(std::string(buffer)); +#else + Kokkos::abort("Kokkos::subview bounds error"); +#endif + } + +#else + + template< size_t ... DimArgs , class ... Args > + KOKKOS_FORCEINLINE_FUNCTION + void error( const ViewDimension< DimArgs ... > & , Args ... ) const {} + +#endif + +public: + + template< size_t ... DimArgs , class ... Args > + KOKKOS_INLINE_FUNCTION + SubviewExtents( const ViewDimension< DimArgs ... > & dim , Args ... args ) + { + static_assert( DomainRank == sizeof...(DimArgs) , "" ); + static_assert( DomainRank == sizeof...(Args) , "" ); + + // Verifies that all arguments, up to 8, are integral types, + // integral extents, or don't exist. + static_assert( RangeRank == + unsigned( is_integral_extent<0,Args...>::value ) + + unsigned( is_integral_extent<1,Args...>::value ) + + unsigned( is_integral_extent<2,Args...>::value ) + + unsigned( is_integral_extent<3,Args...>::value ) + + unsigned( is_integral_extent<4,Args...>::value ) + + unsigned( is_integral_extent<5,Args...>::value ) + + unsigned( is_integral_extent<6,Args...>::value ) + + unsigned( is_integral_extent<7,Args...>::value ) , "" ); + + if ( RangeRank == 0 ) { m_length[0] = 0 ; m_index[0] = ~0u ; } + + if ( ! set( 0 , 0 , dim , args... ) ) error( dim , args... ); + } + + template < typename iType > + KOKKOS_FORCEINLINE_FUNCTION + constexpr size_t domain_offset( const iType i ) const + { return unsigned(i) < DomainRank ? m_begin[i] : 0 ; } + + template < typename iType > + KOKKOS_FORCEINLINE_FUNCTION + constexpr size_t range_extent( const iType i ) const + { return unsigned(i) < InternalRangeRank ? m_length[i] : 0 ; } + + template < typename iType > + KOKKOS_FORCEINLINE_FUNCTION + constexpr unsigned range_index( const iType i ) const + { return unsigned(i) < InternalRangeRank ? m_index[i] : ~0u ; } }; }}} // namespace Kokkos::Experimental::Impl @@ -472,92 +620,25 @@ namespace Experimental { namespace Impl { /** \brief Given a value type and dimension generate the View data type */ -template< class T , class Dim /* ViewDimension */ > -struct ViewDataType { - enum { R = Dim::rank }; - enum { RD = Dim::rank_dynamic }; +template< class T , class Dim > +struct ViewDataType ; - // Unused static dimensions are set to 1 (instead of 0 or -1L) to avoid compile errors - // in the 'false' clauses of the std::conditional. +template< class T > +struct ViewDataType< T , ViewDimension<> > +{ + typedef T type ; +}; - enum { N0 = 0 < Dim::arg_N0 ? Dim::arg_N0 : 1 }; - enum { N1 = 0 < Dim::arg_N1 ? Dim::arg_N1 : 1 }; - enum { N2 = 0 < Dim::arg_N2 ? Dim::arg_N2 : 1 }; - enum { N3 = 0 < Dim::arg_N3 ? Dim::arg_N3 : 1 }; - enum { N4 = 0 < Dim::arg_N4 ? Dim::arg_N4 : 1 }; - enum { N5 = 0 < Dim::arg_N5 ? Dim::arg_N5 : 1 }; - enum { N6 = 0 < Dim::arg_N6 ? Dim::arg_N6 : 1 }; - enum { N7 = 0 < Dim::arg_N7 ? Dim::arg_N7 : 1 }; +template< class T , size_t ... Args > +struct ViewDataType< T , ViewDimension< 0 , Args... > > +{ + typedef typename ViewDataType >::type type ; +}; - typedef typename std::conditional< R == 0 , T , - typename std::conditional< R == 1 , - typename std::conditional< RD == 0 , T[N0] , T * >::type , - - typename std::conditional< R == 2 , - typename std::conditional< RD == 0 , T[N0][N1] , - typename std::conditional< RD == 1 , T* [N1] , - T** - >::type >::type , - - typename std::conditional< R == 3 , - typename std::conditional< RD == 0 , T[N0][N1][N2] , - typename std::conditional< RD == 1 , T* [N1][N2] , - typename std::conditional< RD == 2 , T** [N2] , - T*** - >::type >::type >::type , - - typename std::conditional< R == 4 , - typename std::conditional< RD == 0 , T[N0][N1][N2][N3] , - typename std::conditional< RD == 1 , T* [N1][N2][N3] , - typename std::conditional< RD == 2 , T** [N2][N3] , - typename std::conditional< RD == 3 , T*** [N3] , - T**** - >::type >::type >::type >::type , - - typename std::conditional< R == 5 , - typename std::conditional< RD == 0 , T[N0][N1][N2][N3][N4] , - typename std::conditional< RD == 1 , T* [N1][N2][N3][N4] , - typename std::conditional< RD == 2 , T** [N2][N3][N4] , - typename std::conditional< RD == 3 , T*** [N3][N4] , - typename std::conditional< RD == 4 , T**** [N4] , - T***** - >::type >::type >::type >::type >::type , - - typename std::conditional< R == 6 , - typename std::conditional< RD == 0 , T[N0][N1][N2][N3][N4][N5] , - typename std::conditional< RD == 1 , T* [N1][N2][N3][N4][N5] , - typename std::conditional< RD == 2 , T** [N2][N3][N4][N5] , - typename std::conditional< RD == 3 , T*** [N3][N4][N5] , - typename std::conditional< RD == 4 , T**** [N4][N5] , - typename std::conditional< RD == 5 , T***** [N5] , - T****** - >::type >::type >::type >::type >::type >::type , - - typename std::conditional< R == 7 , - typename std::conditional< RD == 0 , T[N0][N1][N2][N3][N4][N5][N6] , - typename std::conditional< RD == 1 , T* [N1][N2][N3][N4][N5][N6] , - typename std::conditional< RD == 2 , T** [N2][N3][N4][N5][N6] , - typename std::conditional< RD == 3 , T*** [N3][N4][N5][N6] , - typename std::conditional< RD == 4 , T**** [N4][N5][N6] , - typename std::conditional< RD == 5 , T***** [N5][N6] , - typename std::conditional< RD == 6 , T****** [N6] , - T******* - >::type >::type >::type >::type >::type >::type >::type , - - typename std::conditional< R == 8 , - typename std::conditional< RD == 0 , T[N0][N1][N2][N3][N4][N5][N6][N7] , - typename std::conditional< RD == 1 , T* [N1][N2][N3][N4][N5][N6][N7] , - typename std::conditional< RD == 2 , T** [N2][N3][N4][N5][N6][N7] , - typename std::conditional< RD == 3 , T*** [N3][N4][N5][N6][N7] , - typename std::conditional< RD == 4 , T**** [N4][N5][N6][N7] , - typename std::conditional< RD == 5 , T***** [N5][N6][N7] , - typename std::conditional< RD == 6 , T****** [N6][N7] , - typename std::conditional< RD == 7 , T******* [N7] , - T******** - >::type >::type >::type >::type >::type >::type >::type >::type , - - void >::type >::type >::type >::type >::type >::type >::type >::type >::type - type ; +template< class T , size_t N , size_t ... Args > +struct ViewDataType< T , ViewDimension< N , Args... > > +{ + typedef typename ViewDataType >::type type[N] ; }; /**\brief Analysis of View data type. @@ -570,103 +651,79 @@ struct ViewDataType { * Provide typedef for the ViewDimension<...> and value_type. */ template< class T > -struct ViewArrayAnalysis +struct ViewArrayAnalysis { -private: - // std::rank, std::extent, and std::remove_all_extents - // consider "const value_type***" to be the type. - - // Strip away pointers and count them - typedef typename std::remove_all_extents< T >::type t_0 ; // brackets removed - typedef typename std::remove_pointer< t_0 >::type t_1 ; - typedef typename std::remove_pointer< t_1 >::type t_2 ; - typedef typename std::remove_pointer< t_2 >::type t_3 ; - typedef typename std::remove_pointer< t_3 >::type t_4 ; - typedef typename std::remove_pointer< t_4 >::type t_5 ; - typedef typename std::remove_pointer< t_5 >::type t_6 ; - typedef typename std::remove_pointer< t_6 >::type t_7 ; - typedef typename std::remove_pointer< t_7 >::type t_8 ; - typedef typename std::remove_pointer< t_8 >::type t_9 ; - typedef typename std::remove_pointer< t_9 >::type t_10 ; - - enum { rank_pointer = - ( ! std::is_pointer< t_0 >::value ? 0 : - ( ! std::is_pointer< t_1 >::value ? 1 : - ( ! std::is_pointer< t_2 >::value ? 2 : - ( ! std::is_pointer< t_3 >::value ? 3 : - ( ! std::is_pointer< t_4 >::value ? 4 : - ( ! std::is_pointer< t_5 >::value ? 5 : - ( ! std::is_pointer< t_6 >::value ? 6 : - ( ! std::is_pointer< t_7 >::value ? 7 : - ( ! std::is_pointer< t_8 >::value ? 8 : - ( ! std::is_pointer< t_9 >::value ? 9 : - ( ! std::is_pointer< t_10 >::value ? 10 : 0x7fffffff ))))))))))) }; - - // The pointer-stripped type t_10 may have been an array typedef of the form 'type[#][#]...' - // Append those dimensions. - - enum { rank_bracket = std::rank< T >::value }; - enum { rank_bracket_nested = std::rank< t_10 >::value }; - enum { rank_base = rank_pointer + rank_bracket }; - enum { rank = rank_pointer + rank_bracket + rank_bracket_nested }; - - static_assert( rank <= 10 , "Maximum ten dimensional array" ); - - enum { extent_0 = 0 < rank_base ? std::extent< T , rank_pointer <= 0 ? 0 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 0 ? 0 - rank_base : 10 >::value }; - - enum { extent_1 = 1 < rank_base ? std::extent< T , rank_pointer <= 1 ? 1 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 1 ? 1 - rank_base : 10 >::value }; - - enum { extent_2 = 2 < rank_base ? std::extent< T , rank_pointer <= 2 ? 2 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 2 ? 2 - rank_base : 10 >::value }; - - enum { extent_3 = 3 < rank_base ? std::extent< T , rank_pointer <= 3 ? 3 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 3 ? 3 - rank_base : 10 >::value }; - - enum { extent_4 = 4 < rank_base ? std::extent< T , rank_pointer <= 4 ? 4 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 4 ? 4 - rank_base : 10 >::value }; - - enum { extent_5 = 5 < rank_base ? std::extent< T , rank_pointer <= 5 ? 5 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 5 ? 5 - rank_base : 10 >::value }; - - enum { extent_6 = 6 < rank_base ? std::extent< T , rank_pointer <= 6 ? 6 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 6 ? 6 - rank_base : 10 >::value }; - - enum { extent_7 = 7 < rank_base ? std::extent< T , rank_pointer <= 7 ? 7 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 7 ? 7 - rank_base : 10 >::value }; - - enum { extent_8 = 8 < rank_base ? std::extent< T , rank_pointer <= 8 ? 8 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 8 ? 8 - rank_base : 10 >::value }; - - enum { extent_9 = 9 < rank_base ? std::extent< T , rank_pointer <= 9 ? 9 - rank_pointer : 10 >::value - : std::extent< t_10 , rank_base <= 9 ? 9 - rank_base : 10 >::value }; - - typedef typename std::remove_all_extents< t_10 >::type base_type ; - - enum { rank_dynamic = rank_pointer ? rank_pointer : ( ( rank_bracket && extent_0 == 0 ) ? 1 : 0 ) }; - -public: - - typedef ViewDimension< ( rank <= 0 ? -1L : extent_0 ) - , ( rank <= 1 ? -1L : extent_1 ) - , ( rank <= 2 ? -1L : extent_2 ) - , ( rank <= 3 ? -1L : extent_3 ) - , ( rank <= 4 ? -1L : extent_4 ) - , ( rank <= 5 ? -1L : extent_5 ) - , ( rank <= 6 ? -1L : extent_6 ) - , ( rank <= 7 ? -1L : extent_7 ) - > dimension ; - - typedef base_type value_type ; - typedef typename std::add_const< base_type >::type const_value_type ; - typedef typename std::remove_const< base_type >::type non_const_value_type ; - - static_assert( unsigned(dimension::rank) == unsigned(rank) , "" ); - static_assert( unsigned(dimension::rank_dynamic) == unsigned(rank_dynamic) , "" ); + typedef T value_type ; + typedef typename std::add_const< T >::type const_value_type ; + typedef typename std::remove_const< T >::type non_const_value_type ; + typedef ViewDimension<> static_dimension ; + typedef ViewDimension<> dynamic_dimension ; + typedef ViewDimension<> dimension ; }; -template< class DataType , class ValueType , class ArrayLayout > +template< class T , size_t N > +struct ViewArrayAnalysis< T[N] > +{ +private: + typedef ViewArrayAnalysis< T > nested ; +public: + typedef typename nested::value_type value_type ; + typedef typename nested::const_value_type const_value_type ; + typedef typename nested::non_const_value_type non_const_value_type ; + + typedef typename nested::static_dimension::template prepend::type + static_dimension ; + + typedef typename nested::dynamic_dimension dynamic_dimension ; + + typedef typename + ViewDimensionJoin< dynamic_dimension , static_dimension >::type + dimension ; +}; + +template< class T > +struct ViewArrayAnalysis< T[] > +{ +private: + typedef ViewArrayAnalysis< T > nested ; + typedef typename nested::dimension nested_dimension ; +public: + typedef typename nested::value_type value_type ; + typedef typename nested::const_value_type const_value_type ; + typedef typename nested::non_const_value_type non_const_value_type ; + + typedef typename nested::dynamic_dimension::template prepend<0>::type + dynamic_dimension ; + + typedef typename nested::static_dimension static_dimension ; + + typedef typename + ViewDimensionJoin< dynamic_dimension , static_dimension >::type + dimension ; +}; + +template< class T > +struct ViewArrayAnalysis< T* > +{ +private: + typedef ViewArrayAnalysis< T > nested ; +public: + typedef typename nested::value_type value_type ; + typedef typename nested::const_value_type const_value_type ; + typedef typename nested::non_const_value_type non_const_value_type ; + + typedef typename nested::dynamic_dimension::template prepend<0>::type + dynamic_dimension ; + + typedef typename nested::static_dimension static_dimension ; + + typedef typename + ViewDimensionJoin< dynamic_dimension , static_dimension >::type + dimension ; +}; + + +template< class DataType , class ArrayLayout , class ValueType > struct ViewDataAnalysis { private: @@ -917,17 +974,10 @@ struct ViewOffset< Dimension , Kokkos::LayoutLeft template< class DimRHS > KOKKOS_INLINE_FUNCTION - constexpr ViewOffset( const ViewOffset< DimRHS , Kokkos::LayoutLeft , void > & rhs - , const size_t n0 - , const size_t - , const size_t - , const size_t - , const size_t - , const size_t - , const size_t - , const size_t - ) - : m_dim( n0, 0, 0, 0, 0, 0, 0, 0 ) + constexpr ViewOffset( + const ViewOffset< DimRHS , Kokkos::LayoutLeft , void > & rhs , + const SubviewExtents< DimRHS::rank , dimension_type::rank > & sub ) + : m_dim( sub.range_extent(0), 0, 0, 0, 0, 0, 0, 0 ) { static_assert( ( 0 == dimension_type::rank ) || ( 1 == dimension_type::rank && 1 == dimension_type::rank_dynamic && 1 <= DimRHS::rank ) @@ -1142,41 +1192,27 @@ public: //---------------------------------------- // Subview construction + // This subview must be 2 == rank and 2 == rank_dynamic + // due to only having stride #0. + // The source dimension #0 must be non-zero for stride-one leading dimension. + // At most subsequent dimension can be non-zero. template< class DimRHS > KOKKOS_INLINE_FUNCTION - constexpr ViewOffset( const ViewOffset< DimRHS , Kokkos::LayoutLeft , void > & rhs - , const size_t aN0 - , const size_t aN1 - , const size_t aN2 - , const size_t aN3 - , const size_t aN4 - , const size_t aN5 - , const size_t aN6 - , const size_t aN7 - ) - : m_dim( aN0 - , ( 1 < DimRHS::rank && aN1 ? aN1 : - ( 2 < DimRHS::rank && aN2 ? aN2 : - ( 3 < DimRHS::rank && aN3 ? aN3 : - ( 4 < DimRHS::rank && aN4 ? aN4 : - ( 5 < DimRHS::rank && aN5 ? aN5 : - ( 6 < DimRHS::rank && aN6 ? aN6 : - ( 7 < DimRHS::rank && aN7 ? aN7 : 0 ))))))) + constexpr ViewOffset + ( const ViewOffset< DimRHS , Kokkos::LayoutLeft , void > & rhs , + const SubviewExtents< DimRHS::rank , dimension_type::rank > & sub ) + : m_dim( sub.range_extent(0) + , sub.range_extent(1) , 0, 0, 0, 0, 0, 0 ) - , m_stride( ( 1 < DimRHS::rank && aN1 ? rhs.stride_1() : - ( 2 < DimRHS::rank && aN2 ? rhs.stride_2() : - ( 3 < DimRHS::rank && aN3 ? rhs.stride_3() : - ( 4 < DimRHS::rank && aN4 ? rhs.stride_4() : - ( 5 < DimRHS::rank && aN5 ? rhs.stride_5() : - ( 6 < DimRHS::rank && aN6 ? rhs.stride_6() : - ( 7 < DimRHS::rank && aN7 ? rhs.stride_7() : 0 ))))))) ) + , m_stride( ( 1 == sub.range_index(1) ? rhs.stride_1() : + ( 2 == sub.range_index(1) ? rhs.stride_2() : + ( 3 == sub.range_index(1) ? rhs.stride_3() : + ( 4 == sub.range_index(1) ? rhs.stride_4() : + ( 5 == sub.range_index(1) ? rhs.stride_5() : + ( 6 == sub.range_index(1) ? rhs.stride_6() : + ( 7 == sub.range_index(1) ? rhs.stride_7() : 0 )))))))) { - // This subview must be 2 == rank and 2 == rank_dynamic - // due to only having stride #0. - // The source dimension #0 must be non-zero for stride-one leading dimension. - // At most subsequent dimension can be non-zero. - static_assert( ( 2 == dimension_type::rank ) && ( 2 == dimension_type::rank_dynamic ) && ( 2 <= DimRHS::rank ) @@ -1391,17 +1427,11 @@ struct ViewOffset< Dimension , Kokkos::LayoutRight template< class DimRHS > KOKKOS_INLINE_FUNCTION - constexpr ViewOffset( const ViewOffset< DimRHS , Kokkos::LayoutRight , void > & rhs - , const size_t n0 - , const size_t - , const size_t - , const size_t - , const size_t - , const size_t - , const size_t - , const size_t - ) - : m_dim( n0, 0, 0, 0, 0, 0, 0, 0 ) + constexpr ViewOffset + ( const ViewOffset< DimRHS , Kokkos::LayoutRight , void > & rhs + , const SubviewExtents< DimRHS::rank , dimension_type::rank > & sub + ) + : m_dim( sub.range_extent(0) , 0, 0, 0, 0, 0, 0, 0 ) { static_assert( ( 0 == dimension_type::rank ) || ( 1 == dimension_type::rank && 1 == dimension_type::rank_dynamic && 1 <= DimRHS::rank ) @@ -1627,39 +1657,20 @@ public: template< class DimRHS > KOKKOS_INLINE_FUNCTION - constexpr ViewOffset( const ViewOffset< DimRHS , Kokkos::LayoutRight , void > & rhs - , const size_t aN0 - , const size_t aN1 - , const size_t aN2 - , const size_t aN3 - , const size_t aN4 - , const size_t aN5 - , const size_t aN6 - , const size_t aN7 - ) - : m_dim( // N0 == First non-zero dimension before the last dimension. - ( 1 < DimRHS::rank && aN0 ? aN0 : - ( 2 < DimRHS::rank && aN1 ? aN1 : - ( 3 < DimRHS::rank && aN2 ? aN2 : - ( 4 < DimRHS::rank && aN3 ? aN3 : - ( 5 < DimRHS::rank && aN4 ? aN4 : - ( 6 < DimRHS::rank && aN5 ? aN5 : - ( 7 < DimRHS::rank && aN6 ? aN6 : 0 ))))))) - , // N1 == Last dimension. - ( 2 == DimRHS::rank ? aN1 : - ( 3 == DimRHS::rank ? aN2 : - ( 4 == DimRHS::rank ? aN3 : - ( 5 == DimRHS::rank ? aN4 : - ( 6 == DimRHS::rank ? aN5 : - ( 7 == DimRHS::rank ? aN6 : aN7 )))))) + constexpr ViewOffset + ( const ViewOffset< DimRHS , Kokkos::LayoutRight , void > & rhs + , const SubviewExtents< DimRHS::rank , dimension_type::rank > & sub + ) + : m_dim( sub.range_extent(0) + , sub.range_extent(1) , 0, 0, 0, 0, 0, 0 ) - , m_stride( ( 1 < DimRHS::rank && aN0 ? rhs.stride_0() : - ( 2 < DimRHS::rank && aN1 ? rhs.stride_1() : - ( 3 < DimRHS::rank && aN2 ? rhs.stride_2() : - ( 4 < DimRHS::rank && aN3 ? rhs.stride_3() : - ( 5 < DimRHS::rank && aN4 ? rhs.stride_4() : - ( 6 < DimRHS::rank && aN5 ? rhs.stride_5() : - ( 7 < DimRHS::rank && aN6 ? rhs.stride_6() : 0 ))))))) ) + , m_stride( 0 == sub.range_index(0) ? rhs.stride_0() : ( + 1 == sub.range_index(0) ? rhs.stride_1() : ( + 2 == sub.range_index(0) ? rhs.stride_2() : ( + 3 == sub.range_index(0) ? rhs.stride_3() : ( + 4 == sub.range_index(0) ? rhs.stride_4() : ( + 5 == sub.range_index(0) ? rhs.stride_5() : ( + 6 == sub.range_index(0) ? rhs.stride_6() : 0 ))))))) { // This subview must be 2 == rank and 2 == rank_dynamic // due to only having stride #0. @@ -2027,198 +2038,50 @@ public: private: - KOKKOS_INLINE_FUNCTION - static constexpr unsigned - count_non_zero( const size_t aN0 = 0 - , const size_t aN1 = 0 - , const size_t aN2 = 0 - , const size_t aN3 = 0 - , const size_t aN4 = 0 - , const size_t aN5 = 0 - , const size_t aN6 = 0 - , const size_t aN7 = 0 - ) + template< class DimRHS , class LayoutRHS > + KOKKOS_INLINE_FUNCTION static + constexpr size_t stride + ( unsigned r , const ViewOffset< DimRHS , LayoutRHS , void > & rhs ) { - return ( aN0 ? 1 : 0 ) + - ( aN1 ? 1 : 0 ) + - ( aN2 ? 1 : 0 ) + - ( aN3 ? 1 : 0 ) + - ( aN4 ? 1 : 0 ) + - ( aN5 ? 1 : 0 ) + - ( aN6 ? 1 : 0 ) + - ( aN7 ? 1 : 0 ); + return r > 7 ? 0 : ( + r == 0 ? rhs.stride_0() : ( + r == 1 ? rhs.stride_1() : ( + r == 2 ? rhs.stride_2() : ( + r == 3 ? rhs.stride_3() : ( + r == 4 ? rhs.stride_4() : ( + r == 5 ? rhs.stride_5() : ( + r == 6 ? rhs.stride_6() : rhs.stride_7() ))))))); } - template< unsigned Rank , unsigned I > - KOKKOS_INLINE_FUNCTION - static constexpr size_t - get_non_zero( const size_t aN0 - , const size_t aN1 - , const size_t aN2 - , const size_t aN3 - , const size_t aN4 - , const size_t aN5 - , const size_t aN6 - , const size_t aN7 - ) - { - return ( 0 < Rank && I < 1 && aN0 ? aN0 : - ( 1 < Rank && I < 2 && I == count_non_zero(aN0) && aN1 ? aN1 : - ( 2 < Rank && I < 3 && I == count_non_zero(aN0,aN1) && aN2 ? aN2 : - ( 3 < Rank && I < 4 && I == count_non_zero(aN0,aN1,aN2) && aN3 ? aN3 : - ( 4 < Rank && I < 5 && I == count_non_zero(aN0,aN1,aN2,aN3) && aN4 ? aN4 : - ( 5 < Rank && I < 6 && I == count_non_zero(aN0,aN1,aN2,aN3,aN4) && aN5 ? aN5 : - ( 6 < Rank && I < 7 && I == count_non_zero(aN0,aN1,aN2,aN3,aN4,aN5) && aN6 ? aN6 : - ( 7 < Rank && I < 8 && I == count_non_zero(aN0,aN1,aN2,aN3,aN4,aN5,aN6) && aN7 ? aN7 : 0 )))))))); - } - - template< unsigned Rank , unsigned I , class DimRHS , class LayoutRHS > - KOKKOS_INLINE_FUNCTION - static constexpr size_t - get_non_zero( const size_t aN0 , const size_t aN1 , const size_t aN2 , const size_t aN3 - , const size_t aN4 , const size_t aN5 , const size_t aN6 , const size_t aN7 - , const ViewOffset< DimRHS , LayoutRHS , void > & rhs ) - { - return ( 0 < Rank && I < 1 && aN0 ? rhs.stride_0() : - ( 1 < Rank && I < 2 && I == count_non_zero(aN0) && aN1 ? rhs.stride_1() : - ( 2 < Rank && I < 3 && I == count_non_zero(aN0,aN1) && aN2 ? rhs.stride_2() : - ( 3 < Rank && I < 4 && I == count_non_zero(aN0,aN1,aN2) && aN3 ? rhs.stride_3() : - ( 4 < Rank && I < 5 && I == count_non_zero(aN0,aN1,aN2,aN3) && aN4 ? rhs.stride_4() : - ( 5 < Rank && I < 6 && I == count_non_zero(aN0,aN1,aN2,aN3,aN4) && aN5 ? rhs.stride_5() : - ( 6 < Rank && I < 7 && I == count_non_zero(aN0,aN1,aN2,aN3,aN4,aN5) && aN6 ? rhs.stride_6() : - ( 7 < Rank && I < 8 && I == count_non_zero(aN0,aN1,aN2,aN3,aN4,aN5,aN6) && aN7 ? rhs.stride_7() : 0 )))))))); - } - - public: template< class DimRHS , class LayoutRHS > KOKKOS_INLINE_FUNCTION - constexpr ViewOffset( const ViewOffset< DimRHS , LayoutRHS , void > & rhs - , const size_t aN0 - , const size_t aN1 - , const size_t aN2 - , const size_t aN3 - , const size_t aN4 - , const size_t aN5 - , const size_t aN6 - , const size_t aN7 - ) - // Contract the non-zero dimensions - : m_dim( ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7 ) + constexpr ViewOffset + ( const ViewOffset< DimRHS , LayoutRHS , void > & rhs + , const SubviewExtents< DimRHS::rank , dimension_type::rank > & sub + ) + // range_extent(r) returns 0 when dimension_type::rank <= r + : m_dim( sub.range_extent(0) + , sub.range_extent(1) + , sub.range_extent(2) + , sub.range_extent(3) + , sub.range_extent(4) + , sub.range_extent(5) + , sub.range_extent(6) + , sub.range_extent(7) ) - , m_stride( ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) - , ViewOffset::template get_non_zero( aN0, aN1, aN2, aN3, aN4, aN5, aN6, aN7, rhs ) + // range_index(r) returns ~0u when dimension_type::rank <= r + , m_stride( stride( sub.range_index(0), rhs ) + , stride( sub.range_index(1), rhs ) + , stride( sub.range_index(2), rhs ) + , stride( sub.range_index(3), rhs ) + , stride( sub.range_index(4), rhs ) + , stride( sub.range_index(5), rhs ) + , stride( sub.range_index(6), rhs ) + , stride( sub.range_index(7), rhs ) ) - { - } - - //---------------------------------------- -}; - -}}} // namespace Kokkos::Experimental::Impl - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { -namespace Experimental { -namespace Impl { - -struct ALL_t { - KOKKOS_INLINE_FUNCTION - constexpr const ALL_t & operator()() const { return *this ; } -}; - -template< class T > -struct ViewOffsetRange { - - static_assert( std::is_integral::value , "Non-range must be an integral type" ); - - enum { is_range = false }; - - KOKKOS_INLINE_FUNCTION static - size_t dimension( size_t const , T const & ) { return 0 ; } - - KOKKOS_INLINE_FUNCTION static - size_t begin( T const & i ) { return size_t(i) ; } -}; - -template<> -struct ViewOffsetRange { - enum { is_range = false }; -}; - -template<> -struct ViewOffsetRange< Kokkos::Experimental::Impl::ALL_t > { - enum { is_range = true }; - - KOKKOS_INLINE_FUNCTION static - size_t dimension( size_t const n , Experimental::Impl::ALL_t const & ) { return n ; } - - KOKKOS_INLINE_FUNCTION static - size_t begin( Experimental::Impl::ALL_t const & ) { return 0 ; } -}; - -template< typename iType > -struct ViewOffsetRange< std::pair > { - - static_assert( std::is_integral::value , "Range bounds must be an integral type" ); - - enum { is_range = true }; - - KOKKOS_INLINE_FUNCTION static - size_t dimension( size_t const n , std::pair const & r ) - { return ( size_t(r.first) < size_t(r.second) && size_t(r.second) <= n ) ? size_t(r.second) - size_t(r.first) : 0 ; } - - KOKKOS_INLINE_FUNCTION static - size_t begin( std::pair const & r ) { return size_t(r.first) ; } -}; - -template< typename iType > -struct ViewOffsetRange< Kokkos::pair > { - - static_assert( std::is_integral::value , "Range bounds must be an integral type" ); - - enum { is_range = true }; - - KOKKOS_INLINE_FUNCTION static - size_t dimension( size_t const n , Kokkos::pair const & r ) - { return ( size_t(r.first) < size_t(r.second) && size_t(r.second) <= n ) ? size_t(r.second) - size_t(r.first) : 0 ; } - - KOKKOS_INLINE_FUNCTION static - size_t begin( Kokkos::pair const & r ) { return size_t(r.first) ; } -}; - -template< typename iType > -struct ViewOffsetRange< std::initializer_list< iType > > { - - static_assert( std::is_integral::value , "Range bounds must be an integral type" ); - - enum { is_range = true }; - - KOKKOS_INLINE_FUNCTION static - size_t dimension( size_t const n , std::initializer_list< iType > const & r ) - { - return ( size_t(r.begin()[0]) < size_t(r.begin()[1]) && size_t(r.begin()[1]) <= n ) - ? size_t(r.begin()[1]) - size_t(r.begin()[0]) : 0 ; - } - - KOKKOS_INLINE_FUNCTION static - size_t begin( std::initializer_list< iType > const & r ) { return size_t(r.begin()[0]) ; } + {} }; }}} // namespace Kokkos::Experimental::Impl @@ -2290,21 +2153,93 @@ namespace Kokkos { namespace Experimental { namespace Impl { -template< class Traits - , bool R0 = false - , bool R1 = false - , bool R2 = false - , bool R3 = false - , bool R4 = false - , bool R5 = false - , bool R6 = false - , bool R7 = false - , typename Enable = void > -struct SubviewMapping ; +//---------------------------------------------------------------------------- +template< class ValueType , class ExecSpace + , bool IsScalar = std::is_scalar< ValueType >::value > +struct ViewValueFunctor ; + +/* + * The construction, assignment to default, and destruction + * are merged into a single functor. + * Primarily to work around an unresolved CUDA back-end bug + * that would lose the destruction cuda device function when + * called from the shared memory tracking destruction. + * Secondarily to have two fewer partial specializations. + */ +template< class ValueType , class ExecSpace > +struct ViewValueFunctor< ValueType , ExecSpace , false > +{ + enum { CONSTRUCT = 0x01 , ASSIGN = 0x02 , DESTROY = 0x04 }; + + ValueType * const ptr ; + int const mode ; + + KOKKOS_INLINE_FUNCTION + void operator()( size_t i ) const + { + if ( mode == CONSTRUCT ) { new (ptr+i) ValueType(); } + else if ( mode == ASSIGN ) { ptr[i] = ValueType(); } + else if ( mode == DESTROY ) { (ptr+i)->~ValueType(); } + } + + ViewValueFunctor( const ExecSpace & arg_space + , ValueType * const arg_ptr + , size_t const arg_n + , int const arg_mode ) + : ptr( arg_ptr ) + , mode( arg_mode ) + { + if ( ! arg_space.in_parallel() ) { + typedef Kokkos::RangePolicy< ExecSpace > PolicyType ; + const Kokkos::Impl::ParallelFor< ViewValueFunctor , PolicyType > + closure( *this , PolicyType( 0 , arg_n ) ); + closure.execute(); + arg_space.fence(); + } + else { + for ( size_t i = 0 ; i < arg_n ; ++i ) operator()(i); + } + } +}; + +template< class ValueType , class ExecSpace > +struct ViewValueFunctor< ValueType , ExecSpace , true > +{ + enum { CONSTRUCT = 0x01 , ASSIGN = 0x02 , DESTROY = 0x04 }; + + ValueType * const ptr ; + int const mode ; + + KOKKOS_INLINE_FUNCTION + void operator()( size_t i ) const { ptr[i] = 0 ; } + + ViewValueFunctor( const ExecSpace & arg_space + , ValueType * const arg_ptr + , size_t const arg_n + , int const arg_mode ) + : ptr( arg_ptr ) + , mode( arg_mode ) + { + if ( mode == CONSTRUCT || mode == ASSIGN ) { + if ( ! arg_space.in_parallel() ) { + typedef Kokkos::RangePolicy< ExecSpace > PolicyType ; + const Kokkos::Impl::ParallelFor< ViewValueFunctor , PolicyType > + closure( *this , PolicyType( 0 , arg_n ) ); + closure.execute(); + arg_space.fence(); + } + else { + for ( size_t i = 0 ; i < arg_n ; ++i ) operator()(i); + } + } + } +}; + +//---------------------------------------------------------------------------- /** \brief View mapping for non-specialized data type and standard layout */ template< class Traits > -class ViewMapping< Traits , void , +class ViewMapping< Traits , typename std::enable_if<( std::is_same< typename Traits::specialize , void >::value && @@ -2315,9 +2250,8 @@ class ViewMapping< Traits , void , { private: - template< class , class , typename > friend class ViewMapping ; - template< class , bool , bool , bool , bool , bool , bool , bool , bool , class > friend struct SubviewMapping ; - template< class , class , class , class > friend class Kokkos::Experimental::View ; + template< class , class ... > friend class ViewMapping ; + template< class , class ... > friend class Kokkos::Experimental::View ; typedef ViewOffset< typename Traits::dimension , typename Traits::array_layout @@ -2363,6 +2297,9 @@ public: KOKKOS_INLINE_FUNCTION constexpr size_t stride_6() const { return m_offset.stride_6(); } KOKKOS_INLINE_FUNCTION constexpr size_t stride_7() const { return m_offset.stride_7(); } + template< typename iType > + KOKKOS_INLINE_FUNCTION void stride( iType * const s ) const { m_offset.stride(s); } + //---------------------------------------- // Range span @@ -2373,15 +2310,14 @@ public: KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { return m_offset.span_is_contiguous(); } typedef typename ViewDataHandle< Traits >::return_type reference_type ; + typedef typename Traits::value_type * pointer_type ; /** \brief If data references are lvalue_reference than can query pointer to memory */ - KOKKOS_INLINE_FUNCTION constexpr typename Traits::value_type * data() const + KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const { - typedef typename Traits::value_type * ptr_type ; - return std::is_lvalue_reference< reference_type >::value - ? (ptr_type) m_handle - : (ptr_type) 0 ; + ? (pointer_type) m_handle + : (pointer_type) 0 ; } //---------------------------------------- @@ -2501,85 +2437,43 @@ public: template< bool AllowPadding > KOKKOS_INLINE_FUNCTION - ViewMapping( void * ptr + ViewMapping( pointer_type ptr , const std::integral_constant & , const size_t N0 , const size_t N1 , const size_t N2 , const size_t N3 , const size_t N4 , const size_t N5 , const size_t N6 , const size_t N7 ) - : m_handle( reinterpret_cast< handle_type >( ptr ) ) + : m_handle( ptr ) , m_offset( std::integral_constant< unsigned , AllowPadding ? sizeof(typename Traits::value_type) : 0 >() , N0, N1, N2, N3, N4, N5, N6, N7 ) {} template< bool AllowPadding > KOKKOS_INLINE_FUNCTION - ViewMapping( void * ptr + ViewMapping( pointer_type ptr , const std::integral_constant & , const typename Traits::array_layout & layout ) - : m_handle( reinterpret_cast< handle_type >( ptr ) ) + : m_handle( ptr ) , m_offset( layout ) {} //---------------------------------------- // If the View is to construct or destroy the elements. - struct FunctorTagConstructScalar {}; - struct FunctorTagConstructNonScalar {}; - struct FunctorTagDestructNonScalar {}; - - KOKKOS_FORCEINLINE_FUNCTION - void operator()( const FunctorTagConstructScalar & , const size_t i ) const - { m_handle[i] = 0 ; } - - KOKKOS_FORCEINLINE_FUNCTION - void operator()( const FunctorTagConstructNonScalar & , const size_t i ) const - { - typedef typename Traits::value_type value_type ; - new( & m_handle[i] ) value_type(); - } - - KOKKOS_FORCEINLINE_FUNCTION - void operator()( const FunctorTagDestructNonScalar & , const size_t i ) const - { - typedef typename Traits::value_type value_type ; - ( & (m_handle[i]) )->~value_type(); - } - template< class ExecSpace > - typename std::enable_if< Kokkos::Impl::is_execution_space::value && - std::is_scalar< typename Traits::value_type >::value >::type - construct( const ExecSpace & space ) const + void construct( const ExecSpace & space ) const { - typedef Kokkos::RangePolicy< ExecSpace , FunctorTagConstructScalar , size_t > Policy ; + typedef typename Traits::value_type value_type ; + typedef ViewValueFunctor< value_type , ExecSpace > FunctorType ; - (void) Kokkos::Impl::ParallelFor< ViewMapping , Policy >( *this , Policy( 0 , m_offset.span() ) ); - ExecSpace::fence(); + (void) FunctorType( space , (value_type *) m_handle , m_offset.span() , FunctorType::CONSTRUCT ); } template< class ExecSpace > - typename std::enable_if< Kokkos::Impl::is_execution_space::value && - ! std::is_scalar< typename Traits::value_type >::value >::type - construct( const ExecSpace & space ) const + void destroy( const ExecSpace & space ) const { - typedef Kokkos::RangePolicy< ExecSpace , FunctorTagConstructNonScalar , size_t > Policy ; + typedef typename Traits::value_type value_type ; + typedef ViewValueFunctor< value_type , ExecSpace > FunctorType ; - (void) Kokkos::Impl::ParallelFor< ViewMapping , Policy >( *this , Policy( 0 , m_offset.span() ) ); - ExecSpace::fence(); - } - - template< class ExecSpace > - typename std::enable_if< Kokkos::Impl::is_execution_space::value && - std::is_scalar< typename Traits::value_type >::value >::type - destroy( const ExecSpace & ) const {} - - template< class ExecSpace > - typename std::enable_if< Kokkos::Impl::is_execution_space::value && - ! std::is_scalar< typename Traits::value_type >::value >::type - destroy( const ExecSpace & space ) const - { - typedef Kokkos::RangePolicy< ExecSpace , FunctorTagDestructNonScalar , size_t > Policy ; - - (void) Kokkos::Impl::ParallelFor< ViewMapping , Policy >( *this , Policy( 0 , m_offset.span() ) ); - ExecSpace::fence(); + (void) FunctorType( space , (value_type *) m_handle , m_offset.span() , FunctorType::DESTROY ); } }; @@ -2609,28 +2503,48 @@ class ViewMapping< DstTraits , SrcTraits , ) )>::type > { +private: + + enum { is_assignable_value_type = + std::is_same< typename DstTraits::value_type + , typename SrcTraits::value_type >::value || + std::is_same< typename DstTraits::value_type + , typename SrcTraits::const_value_type >::value }; + + enum { is_assignable_dimension = + ViewDimensionAssignable< typename DstTraits::dimension + , typename SrcTraits::dimension >::value }; + + enum { is_assignable_layout = + std::is_same< typename DstTraits::array_layout + , typename SrcTraits::array_layout >::value || + std::is_same< typename DstTraits::array_layout + , Kokkos::LayoutStride >::value || + ( DstTraits::dimension::rank == 0 ) || + ( DstTraits::dimension::rank == 1 && + DstTraits::dimension::rank_dynamic == 1 ) + }; + public: - enum { is_assignable = true }; + enum { is_assignable = is_assignable_value_type && + is_assignable_dimension && + is_assignable_layout }; typedef Kokkos::Experimental::Impl::SharedAllocationTracker TrackType ; - typedef ViewMapping< DstTraits , void , void > DstType ; - typedef ViewMapping< SrcTraits , void , void > SrcType ; + typedef ViewMapping< DstTraits , void > DstType ; + typedef ViewMapping< SrcTraits , void > SrcType ; KOKKOS_INLINE_FUNCTION static void assign( DstType & dst , const SrcType & src , const TrackType & src_track ) { - static_assert( std::is_same< typename DstTraits::value_type , typename SrcTraits::value_type >::value || - std::is_same< typename DstTraits::value_type , typename SrcTraits::const_value_type >::value + static_assert( is_assignable_value_type , "View assignment must have same value type or const = non-const" ); - static_assert( ViewDimensionAssignable< typename DstTraits::dimension , typename SrcTraits::dimension >::value + static_assert( is_assignable_dimension , "View assignment must have compatible dimensions" ); - static_assert( std::is_same< typename DstTraits::array_layout , typename SrcTraits::array_layout >::value || - std::is_same< typename DstTraits::array_layout , Kokkos::LayoutStride >::value || - ( DstTraits::dimension::rank == 0 ) || - ( DstTraits::dimension::rank == 1 && DstTraits::dimension::rank_dynamic == 1 ) + static_assert( is_assignable_layout , "View assignment must have compatible layout or have rank <= 1" ); typedef typename DstType::offset_type dst_offset_type ; @@ -2642,35 +2556,55 @@ public: //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- +// Subview mapping. +// Deduce destination view type from source view traits and subview arguments -/** \brief View mapping for non-specialized data type and standard layout */ -template< class Traits , bool R0 , bool R1 , bool R2 , bool R3 , bool R4 , bool R5 , bool R6 , bool R7 > -struct SubviewMapping< Traits, R0, R1, R2, R3, R4, R5, R6, R7 , - typename std::enable_if<( - std::is_same< typename Traits::specialize , void >::value - && - ( - std::is_same< typename Traits::array_layout , Kokkos::LayoutLeft >::value || - std::is_same< typename Traits::array_layout , Kokkos::LayoutRight >::value || - std::is_same< typename Traits::array_layout , Kokkos::LayoutStride >::value - ) - )>::type > +template< class SrcTraits , class ... Args > +struct ViewMapping + < typename std::enable_if<( + std::is_same< typename SrcTraits::specialize , void >::value + && + ( + std::is_same< typename SrcTraits::array_layout + , Kokkos::LayoutLeft >::value || + std::is_same< typename SrcTraits::array_layout + , Kokkos::LayoutRight >::value || + std::is_same< typename SrcTraits::array_layout + , Kokkos::LayoutStride >::value + ) + )>::type + , SrcTraits + , Args ... > { private: - // Subview's rank + static_assert( SrcTraits::rank == sizeof...(Args) , + "Subview mapping requires one argument for each dimension of source View" ); + + enum + { RZ = false + , R0 = bool(is_integral_extent<0,Args...>::value) + , R1 = bool(is_integral_extent<1,Args...>::value) + , R2 = bool(is_integral_extent<2,Args...>::value) + , R3 = bool(is_integral_extent<3,Args...>::value) + , R4 = bool(is_integral_extent<4,Args...>::value) + , R5 = bool(is_integral_extent<5,Args...>::value) + , R6 = bool(is_integral_extent<6,Args...>::value) + , R7 = bool(is_integral_extent<7,Args...>::value) + }; + enum { rank = unsigned(R0) + unsigned(R1) + unsigned(R2) + unsigned(R3) + unsigned(R4) + unsigned(R5) + unsigned(R6) + unsigned(R7) }; // Whether right-most rank is a range. - enum { R0_rev = 0 == Traits::rank ? false : ( - 1 == Traits::rank ? R0 : ( - 2 == Traits::rank ? R1 : ( - 3 == Traits::rank ? R2 : ( - 4 == Traits::rank ? R3 : ( - 5 == Traits::rank ? R4 : ( - 6 == Traits::rank ? R5 : ( - 7 == Traits::rank ? R6 : R7 ))))))) }; + enum { R0_rev = ( 0 == SrcTraits::rank ? RZ : ( + 1 == SrcTraits::rank ? R0 : ( + 2 == SrcTraits::rank ? R1 : ( + 3 == SrcTraits::rank ? R2 : ( + 4 == SrcTraits::rank ? R3 : ( + 5 == SrcTraits::rank ? R4 : ( + 6 == SrcTraits::rank ? R5 : ( + 7 == SrcTraits::rank ? R6 : R7 )))))))) }; // Subview's layout typedef typename std::conditional< @@ -2679,15 +2613,15 @@ private: || // OutputRank 1 or 2, InputLayout Left, Interval 0 // because single stride one or second index has a stride. - ( rank <= 2 && R0 && std::is_same< typename Traits::array_layout , Kokkos::LayoutLeft >::value ) + ( rank <= 2 && R0 && std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutLeft >::value ) || // OutputRank 1 or 2, InputLayout Right, Interval [InputRank-1] // because single stride one or second index has a stride. - ( rank <= 2 && R0_rev && std::is_same< typename Traits::array_layout , Kokkos::LayoutRight >::value ) - ), typename Traits::array_layout , Kokkos::LayoutStride + ( rank <= 2 && R0_rev && std::is_same< typename SrcTraits::array_layout , Kokkos::LayoutRight >::value ) + ), typename SrcTraits::array_layout , Kokkos::LayoutStride >::type array_layout ; - typedef typename Traits::value_type value_type ; + typedef typename SrcTraits::value_type value_type ; typedef typename std::conditional< rank == 0 , value_type , typename std::conditional< rank == 1 , value_type * , @@ -2703,66 +2637,66 @@ private: public: - typedef - Kokkos::Experimental::ViewTraits< data_type , array_layout - , typename Traits::device_type - , typename Traits::memory_traits > traits_type ; + typedef Kokkos::Experimental::ViewTraits + < data_type + , array_layout + , typename SrcTraits::device_type + , typename SrcTraits::memory_traits > traits_type ; - typedef Kokkos::Experimental::View< data_type - , array_layout - , typename Traits::device_type - , typename Traits::memory_traits > type ; + typedef Kokkos::Experimental::View + < data_type + , array_layout + , typename SrcTraits::device_type + , typename SrcTraits::memory_traits > type ; - template< class T0 , class T1 , class T2 , class T3 - , class T4 , class T5 , class T6 , class T7 > + template< class MemoryTraits > + struct apply { + + static_assert( Kokkos::Impl::is_memory_traits< MemoryTraits >::value , "" ); + + typedef Kokkos::Experimental::ViewTraits + < data_type + , array_layout + , typename SrcTraits::device_type + , MemoryTraits > traits_type ; + + typedef Kokkos::Experimental::View + < data_type + , array_layout + , typename SrcTraits::device_type + , MemoryTraits > type ; + }; + + // The presumed type is 'ViewMapping< traits_type , void >' + // However, a compatible ViewMapping is acceptable. + template< class DstTraits > KOKKOS_INLINE_FUNCTION - static void assign( ViewMapping< traits_type , void , void > & dst - , ViewMapping< Traits , void , void > const & src - , T0 const & arg0 - , T1 const & arg1 - , T2 const & arg2 - , T3 const & arg3 - , T4 const & arg4 - , T5 const & arg5 - , T6 const & arg6 - , T7 const & arg7 - ) + static void assign( ViewMapping< DstTraits , void > & dst + , ViewMapping< SrcTraits , void > const & src + , Args ... args ) { - typedef ViewMapping< traits_type , void , void > DstType ; + static_assert( + ViewMapping< DstTraits , traits_type , void >::is_assignable , + "Subview destination type must be compatible with subview derived type" ); + + typedef ViewMapping< DstTraits , void > DstType ; typedef typename DstType::offset_type dst_offset_type ; typedef typename DstType::handle_type dst_handle_type ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V0 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V1 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V2 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V3 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V4 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V5 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V6 ; - typedef Kokkos::Experimental::Impl::ViewOffsetRange V7 ; - - dst.m_offset = dst_offset_type - ( src.m_offset - , V0::dimension( src.m_offset.dimension_0() , arg0 ) - , V1::dimension( src.m_offset.dimension_1() , arg1 ) - , V2::dimension( src.m_offset.dimension_2() , arg2 ) - , V3::dimension( src.m_offset.dimension_3() , arg3 ) - , V4::dimension( src.m_offset.dimension_4() , arg4 ) - , V5::dimension( src.m_offset.dimension_5() , arg5 ) - , V6::dimension( src.m_offset.dimension_6() , arg6 ) - , V7::dimension( src.m_offset.dimension_7() , arg7 ) - ); + const SubviewExtents< SrcTraits::rank , rank > + extents( src.m_offset.m_dim , args... ); + dst.m_offset = dst_offset_type( src.m_offset , extents ); dst.m_handle = dst_handle_type( src.m_handle + - src.m_offset( V0::begin( arg0 ) - , V1::begin( arg1 ) - , V2::begin( arg2 ) - , V3::begin( arg3 ) - , V4::begin( arg4 ) - , V5::begin( arg5 ) - , V6::begin( arg6 ) - , V7::begin( arg7 ) + src.m_offset( extents.domain_offset(0) + , extents.domain_offset(1) + , extents.domain_offset(2) + , extents.domain_offset(3) + , extents.domain_offset(4) + , extents.domain_offset(5) + , extents.domain_offset(6) + , extents.domain_offset(7) ) ); } }; @@ -2776,53 +2710,12 @@ namespace Kokkos { namespace Experimental { namespace Impl { -template< class V - , bool R0 = false , bool R1 = false , bool R2 = false , bool R3 = false - , bool R4 = false , bool R5 = false , bool R6 = false , bool R7 = false > -struct SubviewType ; - -template< class D , class A1, class A2, class A3 - , bool R0 , bool R1 , bool R2 , bool R3 - , bool R4 , bool R5 , bool R6 , bool R7 > -struct SubviewType< Kokkos::Experimental::View< D , A1, A2, A3 > , R0 , R1 , R2 , R3 , R4 , R5 , R6 , R7 > -{ -private: - typedef Kokkos::Experimental::ViewTraits< D , A1 , A2 , A3 > traits ; - typedef Kokkos::Experimental::Impl::SubviewMapping< traits , R0 , R1 , R2 , R3 , R4 , R5 , R6 , R7 > mapping ; -public: - typedef typename mapping::type type ; -}; - -}}} // namespace Kokkos::Experimental::Impl - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { -namespace Experimental { -namespace Impl { - class Error_view_scalar_reference_to_non_scalar_view ; } /* namespace Impl */ } /* namespace Experimental */ } /* namespace Kokkos */ -#if defined( KOKKOS_EXPRESSION_CHECK ) - -#define KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( SPACE , MAP , RANK , I0 , I1 , I2 , I3 , I4 , I5 , I6 , I7 ) \ - Kokkos::Impl::VerifyExecutionCanAccessMemorySpace< \ - Kokkos::Impl::ActiveExecutionMemorySpace , SPACE >::verify( MAP.data() ); \ - /* array bounds checking */ - -#else - -#define KOKKOS_ASSERT_VIEW_MAPPING_ACCESS( SPACE , MAP , RANK , I0 , I1 , I2 , I3 , I4 , I5 , I6 , I7 ) \ - Kokkos::Impl::VerifyExecutionCanAccessMemorySpace< \ - Kokkos::Impl::ActiveExecutionMemorySpace , SPACE >::verify( MAP.data() ) - -#endif - //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- diff --git a/lib/kokkos/core/src/impl/KokkosExp_ViewTile.hpp b/lib/kokkos/core/src/impl/KokkosExp_ViewTile.hpp index a661a35347..32bd7bac91 100644 --- a/lib/kokkos/core/src/impl/KokkosExp_ViewTile.hpp +++ b/lib/kokkos/core/src/impl/KokkosExp_ViewTile.hpp @@ -69,8 +69,8 @@ struct ViewOffset< Dimension , Layout , { public: - enum { SHIFT_0 = Kokkos::Impl::power_of_two::value }; - enum { SHIFT_1 = Kokkos::Impl::power_of_two::value }; + enum { SHIFT_0 = Kokkos::Impl::integral_power_of_two(Layout::N0) }; + enum { SHIFT_1 = Kokkos::Impl::integral_power_of_two(Layout::N1) }; enum { SHIFT_T = SHIFT_0 + SHIFT_1 }; enum { MASK_0 = Layout::N0 - 1 }; enum { MASK_1 = Layout::N1 - 1 }; @@ -155,6 +155,42 @@ public: {} }; +template< typename T , unsigned N0 , unsigned N1 , class ... P + , typename iType0 , typename iType1 + > +struct ViewMapping + < void + , Kokkos::Experimental::ViewTraits,P...> + , Kokkos::LayoutTileLeft + , iType0 + , iType1 > +{ + typedef Kokkos::LayoutTileLeft src_layout ; + typedef Kokkos::Experimental::ViewTraits< T** , src_layout , P... > src_traits ; + typedef Kokkos::Experimental::ViewTraits< T[N0][N1] , LayoutLeft , P ... > traits ; + typedef Kokkos::Experimental::View< T[N0][N1] , LayoutLeft , P ... > type ; + + KOKKOS_INLINE_FUNCTION static + void assign( ViewMapping< traits , void > & dst + , const ViewMapping< src_traits , void > & src + , const src_layout & + , const size_t i_tile0 + , const size_t i_tile1 + ) + { + typedef ViewMapping< traits , void > dst_map_type ; + typedef ViewMapping< src_traits , void > src_map_type ; + typedef typename dst_map_type::handle_type dst_handle_type ; + typedef typename dst_map_type::offset_type dst_offset_type ; + typedef typename src_map_type::offset_type src_offset_type ; + + dst = dst_map_type( + dst_handle_type( src.m_handle + + ( ( i_tile0 + src.m_offset.m_tile_N0 * i_tile1 ) << src_offset_type::SHIFT_T ) ) , + dst_offset_type() ); + } +}; + } /* namespace Impl */ } /* namespace Experimental */ } /* namespace Kokkos */ @@ -162,51 +198,20 @@ public: namespace Kokkos { namespace Experimental { -// Using View with an invalid data type to construct the tiling subview. -// View is a friend of View so we use this invalid data type partial specialization -// to access implementation of both source and destination view for constructing -// the tile subview. - -template< unsigned N0 , unsigned N1 > -struct View< void , Kokkos::LayoutTileLeft , void , void > -{ - typedef Kokkos::LayoutTileLeft Layout ; - - template< typename T , class A2 , class A3 > - KOKKOS_INLINE_FUNCTION static - Kokkos::Experimental::View< T[N0][N1] , LayoutLeft , A2 , A3 > - tile_subview( const Kokkos::Experimental::View & src - , const size_t i_tile0 - , const size_t i_tile1 - ) - { - typedef Kokkos::Experimental::View SrcView ; - typedef Kokkos::Experimental::View< T[N0][N1] , LayoutLeft , A2 , A3 > DstView ; - - typedef typename SrcView::map_type::offset_type src_offset_type ; - typedef typename DstView::map_type dst_map_type ; - typedef typename DstView::map_type::handle_type dst_handle_type ; - typedef typename DstView::map_type::offset_type dst_offset_type ; - - return DstView( src.m_track , - dst_map_type( - dst_handle_type( src.m_map.m_handle + - ( ( i_tile0 + src.m_map.m_offset.m_tile_N0 * i_tile1 ) << src_offset_type::SHIFT_T ) ) , - dst_offset_type() ) - ); - } -}; - -template< typename T , unsigned N0 , unsigned N1 , class A2 , class A3 > +template< typename T , unsigned N0 , unsigned N1 , class ... P > KOKKOS_INLINE_FUNCTION -Kokkos::Experimental::View< T[N0][N1] , LayoutLeft , A2 , A3 > -tile_subview( const Kokkos::Experimental::View,A2,A3> & src +Kokkos::Experimental::View< T[N0][N1] , LayoutLeft , P... > +tile_subview( const Kokkos::Experimental::View,P...> & src , const size_t i_tile0 , const size_t i_tile1 ) { - return View< void , Kokkos::LayoutTileLeft , void , void >:: - tile_subview( src , i_tile0 , i_tile1 ); + // Force the specialized ViewMapping for extracting a tile + // by using the first subview argument as the layout. + typedef Kokkos::LayoutTileLeft SrcLayout ; + + return Kokkos::Experimental::View< T[N0][N1] , LayoutLeft , P... > + ( src , SrcLayout() , i_tile0 , i_tile1 ); } } /* namespace Experimental */ diff --git a/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.cpp b/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.cpp index 7fb33853d6..efd2a096ad 100644 --- a/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.cpp @@ -43,6 +43,8 @@ #include +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + #if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) #include @@ -842,3 +844,5 @@ void * create_singleton( size_t size #endif /* #if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) */ +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + diff --git a/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.hpp b/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.hpp index 331c4e8fac..8912d73bab 100644 --- a/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_AllocationTracker.hpp @@ -46,6 +46,8 @@ #include +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + #include #include @@ -351,7 +353,6 @@ public: //----------------------------------------------------------------------------- // forward declaration for friend classes -struct CopyWithoutTracking; struct MallocHelper; /// class AllocationTracker @@ -544,6 +545,10 @@ public: /// NOT thread-safe void reallocate( size_t size ) const; + static void disable_tracking(); + static void enable_tracking(); + static bool tracking_enabled(); + private: static AllocationTracker find( void const * ptr, AllocatorBase const * arg_allocator ); @@ -556,31 +561,14 @@ private: void increment_ref_count() const; void decrement_ref_count() const; - static void disable_tracking(); - static void enable_tracking(); - static bool tracking_enabled(); - - friend struct Impl::CopyWithoutTracking; friend struct Impl::MallocHelper; uintptr_t m_alloc_rec; }; - - -/// Make a copy of the functor with reference counting disabled -struct CopyWithoutTracking -{ - template - static Functor apply( const Functor & f ) - { - AllocationTracker::disable_tracking(); - Functor func(f); - AllocationTracker::enable_tracking(); - return func; - } -}; - }} // namespace Kokkos::Impl +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + #endif //KOKKOS_ALLOCATION_TRACKER_HPP + diff --git a/lib/kokkos/core/src/impl/Kokkos_Atomic_View.hpp b/lib/kokkos/core/src/impl/Kokkos_Atomic_View.hpp index f95ed67da9..b1d47e19fb 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Atomic_View.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Atomic_View.hpp @@ -427,6 +427,8 @@ struct Kokkos_Atomic_is_only_allowed_with_32bit_and_64bit_scalars<8> { typedef int64_t type; }; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + // Must be non-const, atomic access trait, and 32 or 64 bit type for true atomics. template class ViewDataHandle< @@ -457,6 +459,8 @@ public: } }; +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + }} // namespace Kokkos::Impl #endif diff --git a/lib/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp b/lib/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp index 62581569fb..14066e8be2 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp @@ -45,6 +45,7 @@ #ifdef _WIN32 #define NOMINMAX +#include #include namespace Kokkos { @@ -61,7 +62,6 @@ namespace Kokkos { }; } -#ifdef KOKKOS_HAVE_CXX11 template < typename T > KOKKOS_INLINE_FUNCTION T atomic_compare_exchange(volatile T * const dest, const T & compare, @@ -103,10 +103,18 @@ namespace Kokkos { KOKKOS_INLINE_FUNCTION U() {}; } tmp, newval; newval.t = val; - tmp.i = _InterlockedCompareExchange128((LONGLONG*)dest, newval.i.upper, newval.i.lower, *((LONGLONG*)&compare)); + _InterlockedCompareExchange128((LONGLONG*)dest, newval.i.upper, newval.i.lower, ((LONGLONG*)&compare)); + tmp.t = dest; return tmp.t; } + template < typename T > + KOKKOS_INLINE_FUNCTION + T atomic_compare_exchange_strong(volatile T * const dest, const T & compare, const T & val) + { + return atomic_compare_exchange(dest,compare,val); + } + template< typename T > T atomic_fetch_or(volatile T * const dest, const T val) { T oldval = *dest; @@ -147,7 +155,20 @@ namespace Kokkos { } template< typename T > - T atomic_fetch_exchange(volatile T * const dest, const T val) { + T atomic_fetch_sub(volatile T * const dest, const T val) { + T oldval = *dest; + T assume; + do { + assume = oldval; + T newval = val - oldval; + oldval = atomic_compare_exchange(dest, assume, newval); + } while (assume != oldval); + + return oldval; + } + + template< typename T > + T atomic_exchange(volatile T * const dest, const T val) { T oldval = *dest; T assume; do { @@ -174,8 +195,8 @@ namespace Kokkos { } template< typename T > - void atomic_exchange(volatile T * const dest, const T val) { - atomic_fetch_exchange(dest, val); + void atomic_sub(volatile T * const dest, const T val) { + atomic_fetch_sub(dest, val); } template< typename T > @@ -208,4 +229,4 @@ namespace Kokkos { } #endif #endif -#endif \ No newline at end of file + diff --git a/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.cpp b/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.cpp index 08085dca36..6562ea700c 100644 --- a/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.cpp @@ -43,6 +43,8 @@ #include +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + #include #include @@ -50,8 +52,11 @@ #include // uintptr_t #include // for malloc, realloc, and free #include // for memcpy + +#if defined(KOKKOS_POSIX_MEMALIGN_AVAILABLE) #include // for mmap, munmap, MAP_ANON, etc #include // for sysconf, _SC_PAGE_SIZE, _SC_PHYS_PAGES +#endif #include @@ -103,8 +108,7 @@ void * raw_aligned_allocate( size_t size, size_t alignment ) #if defined( __INTEL_COMPILER ) && !defined ( KOKKOS_HAVE_CUDA ) ptr = _mm_malloc( size , alignment ); -#elif ( defined( _POSIX_C_SOURCE ) && _POSIX_C_SOURCE >= 200112L ) || \ - ( defined( _XOPEN_SOURCE ) && _XOPEN_SOURCE >= 600 ) +#elif defined(KOKKOS_POSIX_MEMALIGN_AVAILABLE) posix_memalign( & ptr, alignment , size ); @@ -136,8 +140,7 @@ void raw_aligned_deallocate( void * ptr, size_t /*size*/ ) #if defined( __INTEL_COMPILER ) && !defined ( KOKKOS_HAVE_CUDA ) _mm_free( ptr ); -#elif ( defined( _POSIX_C_SOURCE ) && _POSIX_C_SOURCE >= 200112L ) || \ - ( defined( _XOPEN_SOURCE ) && _XOPEN_SOURCE >= 600 ) +#elif defined(KOKKOS_POSIX_MEMALIGN_AVAILABLE) free( ptr ); #else // get the alloc'd pointer @@ -279,3 +282,6 @@ void * PageAlignedAllocator::reallocate(void * old_ptr, size_t old_size, size_t } }} // namespace Kokkos::Impl + +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + diff --git a/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.hpp b/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.hpp index 76377c5f15..43a150fb4a 100644 --- a/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_BasicAllocators.hpp @@ -44,6 +44,7 @@ #ifndef KOKKOS_BASIC_ALLOCATORS_HPP #define KOKKOS_BASIC_ALLOCATORS_HPP +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) namespace Kokkos { namespace Impl { @@ -113,6 +114,8 @@ public: }} // namespace Kokkos::Impl +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + #endif //KOKKOS_BASIC_ALLOCATORS_HPP diff --git a/lib/kokkos/core/src/impl/Kokkos_Core.cpp b/lib/kokkos/core/src/impl/Kokkos_Core.cpp index 1c3c83cfe7..bb0ce3f834 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Core.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Core.cpp @@ -67,6 +67,13 @@ bool is_unsigned_int(const char* str) void initialize_internal(const InitArguments& args) { +// This is an experimental setting +// For KNL in Flat mode this variable should be set, so that +// memkind allocates high bandwidth memory correctly. +#ifdef KOKKOS_HAVE_HBWSPACE +setenv("MEMKIND_HBW_NODES", "1", 0); +#endif + // Protect declarations, to prevent "unused variable" warnings. #if defined( KOKKOS_HAVE_OPENMP ) || defined( KOKKOS_HAVE_PTHREAD ) const int num_threads = args.num_threads; diff --git a/lib/kokkos/core/src/impl/Kokkos_Error.cpp b/lib/kokkos/core/src/impl/Kokkos_Error.cpp index 97cfbfae7e..36224990d0 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Error.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Error.cpp @@ -61,7 +61,7 @@ void host_abort( const char * const message ) { fwrite(message,1,strlen(message),stderr); fflush(stderr); - abort(); + ::abort(); } void throw_runtime_exception( const std::string & msg ) diff --git a/lib/kokkos/core/src/impl/Kokkos_Error.hpp b/lib/kokkos/core/src/impl/Kokkos_Error.hpp index 33e203c948..5f88d66206 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Error.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Error.hpp @@ -46,6 +46,10 @@ #include #include +#include +#ifdef KOKKOS_HAVE_CUDA +#include +#endif namespace Kokkos { namespace Impl { diff --git a/lib/kokkos/core/src/impl/Kokkos_HBWAllocators.cpp b/lib/kokkos/core/src/impl/Kokkos_HBWAllocators.cpp new file mode 100644 index 0000000000..4eb80d03f1 --- /dev/null +++ b/lib/kokkos/core/src/impl/Kokkos_HBWAllocators.cpp @@ -0,0 +1,108 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 2.0 +// Copyright (2014) Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +// 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. +// +// Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#include + +#include +#include + + +#include // uintptr_t +#include // for malloc, realloc, and free +#include // for memcpy + +#if defined(KOKKOS_POSIX_MEMALIGN_AVAILABLE) +#include // for mmap, munmap, MAP_ANON, etc +#include // for sysconf, _SC_PAGE_SIZE, _SC_PHYS_PAGES +#endif + +#include +#include + +#ifdef KOKKOS_HAVE_HBWSPACE +#include + +namespace Kokkos { +namespace Experimental { +namespace Impl { +#define MEMKIND_TYPE MEMKIND_HBW //hbw_get_kind(HBW_PAGESIZE_4KB) +/*--------------------------------------------------------------------------*/ + +void* HBWMallocAllocator::allocate( size_t size ) +{ + std::cout<< "Allocate HBW: " << 1.0e-6*size << "MB" << std::endl; + void * ptr = NULL; + if (size) { + ptr = memkind_malloc(MEMKIND_TYPE,size); + + if (!ptr) + { + std::ostringstream msg ; + msg << name() << ": allocate(" << size << ") FAILED"; + Kokkos::Impl::throw_runtime_exception( msg.str() ); + } + } + return ptr; +} + +void HBWMallocAllocator::deallocate( void * ptr, size_t /*size*/ ) +{ + if (ptr) { + memkind_free(MEMKIND_TYPE,ptr); + } +} + +void * HBWMallocAllocator::reallocate(void * old_ptr, size_t /*old_size*/, size_t new_size) +{ + void * ptr = memkind_realloc(MEMKIND_TYPE, old_ptr, new_size); + + if (new_size > 0u && ptr == NULL) { + Kokkos::Impl::throw_runtime_exception("Error: Malloc Allocator could not reallocate memory"); + } + return ptr; +} + +} // namespace Impl +} // namespace Experimental +} // namespace Kokkos +#endif diff --git a/lib/kokkos/core/src/impl/Kokkos_HBWAllocators.hpp b/lib/kokkos/core/src/impl/Kokkos_HBWAllocators.hpp new file mode 100644 index 0000000000..be0134460b --- /dev/null +++ b/lib/kokkos/core/src/impl/Kokkos_HBWAllocators.hpp @@ -0,0 +1,75 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 2.0 +// Copyright (2014) Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +// 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. +// +// Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#ifndef KOKKOS_HBW_ALLOCATORS_HPP +#define KOKKOS_HBW_ALLOCATORS_HPP + +#ifdef KOKKOS_HAVE_HBWSPACE + +namespace Kokkos { +namespace Experimental { +namespace Impl { + +/// class MallocAllocator +class HBWMallocAllocator +{ +public: + static const char * name() + { + return "HBW Malloc Allocator"; + } + + static void* allocate(size_t size); + + static void deallocate(void * ptr, size_t size); + + static void * reallocate(void * old_ptr, size_t old_size, size_t new_size); +}; + +} +} +} // namespace Kokkos::Impl +#endif //KOKKOS_HAVE_HBWSPACE +#endif //KOKKOS_HBW_ALLOCATORS_HPP + + diff --git a/lib/kokkos/core/src/impl/Kokkos_HBWSpace.cpp b/lib/kokkos/core/src/impl/Kokkos_HBWSpace.cpp new file mode 100644 index 0000000000..68e424e859 --- /dev/null +++ b/lib/kokkos/core/src/impl/Kokkos_HBWSpace.cpp @@ -0,0 +1,397 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 2.0 +// Copyright (2014) Sandia Corporation +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE +// 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. +// +// Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + + +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef KOKKOS_HAVE_HBWSPACE +#include +#endif + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +#ifdef KOKKOS_HAVE_HBWSPACE +#define MEMKIND_TYPE MEMKIND_HBW //hbw_get_kind(HBW_PAGESIZE_4KB) + +namespace Kokkos { +namespace Experimental { +namespace { + +static const int QUERY_SPACE_IN_PARALLEL_MAX = 16 ; + +typedef int (* QuerySpaceInParallelPtr )(); + +QuerySpaceInParallelPtr s_in_parallel_query[ QUERY_SPACE_IN_PARALLEL_MAX ] ; +int s_in_parallel_query_count = 0 ; + +} // namespace + +void HBWSpace::register_in_parallel( int (*device_in_parallel)() ) +{ + if ( 0 == device_in_parallel ) { + Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::HBWSpace::register_in_parallel ERROR : given NULL" ) ); + } + + int i = -1 ; + + if ( ! (device_in_parallel)() ) { + for ( i = 0 ; i < s_in_parallel_query_count && ! (*(s_in_parallel_query[i]))() ; ++i ); + } + + if ( i < s_in_parallel_query_count ) { + Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::HBWSpace::register_in_parallel_query ERROR : called in_parallel" ) ); + + } + + if ( QUERY_SPACE_IN_PARALLEL_MAX <= i ) { + Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::HBWSpace::register_in_parallel_query ERROR : exceeded maximum" ) ); + + } + + for ( i = 0 ; i < s_in_parallel_query_count && s_in_parallel_query[i] != device_in_parallel ; ++i ); + + if ( i == s_in_parallel_query_count ) { + s_in_parallel_query[s_in_parallel_query_count++] = device_in_parallel ; + } +} + +int HBWSpace::in_parallel() +{ + const int n = s_in_parallel_query_count ; + + int i = 0 ; + + while ( i < n && ! (*(s_in_parallel_query[i]))() ) { ++i ; } + + return i < n ; +} + +} // namespace Experiemtal +} // namespace Kokkos + +/*--------------------------------------------------------------------------*/ + +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + +namespace Kokkos { +namespace Experimental { + +Kokkos::Impl::AllocationTracker HBWSpace::allocate_and_track( const std::string & label, const size_t size ) +{ + return Kokkos::Impl::AllocationTracker( allocator(), size, label ); +} + +} // namespace Experimental +} // namespace Kokkos + +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + +/*--------------------------------------------------------------------------*/ + +namespace Kokkos { +namespace Experimental { + +/* Default allocation mechanism */ +HBWSpace::HBWSpace() + : m_alloc_mech( + HBWSpace::STD_MALLOC + ) +{ +printf("Init\n"); +setenv("MEMKIND_HBW_NODES", "1", 0); +} + +/* Default allocation mechanism */ +HBWSpace::HBWSpace( const HBWSpace::AllocationMechanism & arg_alloc_mech ) + : m_alloc_mech( HBWSpace::STD_MALLOC ) +{ +printf("Init2\n"); +setenv("MEMKIND_HBW_NODES", "1", 0); + if ( arg_alloc_mech == STD_MALLOC ) { + m_alloc_mech = HBWSpace::STD_MALLOC ; + } +} + +void * HBWSpace::allocate( const size_t arg_alloc_size ) const +{ + static_assert( sizeof(void*) == sizeof(uintptr_t) + , "Error sizeof(void*) != sizeof(uintptr_t)" ); + + static_assert( Kokkos::Impl::power_of_two< Kokkos::Impl::MEMORY_ALIGNMENT >::value + , "Memory alignment must be power of two" ); + + constexpr uintptr_t alignment = Kokkos::Impl::MEMORY_ALIGNMENT ; + constexpr uintptr_t alignment_mask = alignment - 1 ; + + void * ptr = 0 ; + + if ( arg_alloc_size ) { + + if ( m_alloc_mech == STD_MALLOC ) { + // Over-allocate to and round up to guarantee proper alignment. + size_t size_padded = arg_alloc_size + sizeof(void*) + alignment ; + + void * alloc_ptr = memkind_malloc(MEMKIND_TYPE, size_padded ); + + if (alloc_ptr) { + uintptr_t address = reinterpret_cast(alloc_ptr); + + // offset enough to record the alloc_ptr + address += sizeof(void *); + uintptr_t rem = address % alignment; + uintptr_t offset = rem ? (alignment - rem) : 0u; + address += offset; + ptr = reinterpret_cast(address); + // record the alloc'd pointer + address -= sizeof(void *); + *reinterpret_cast(address) = alloc_ptr; + } + } + } + + if ( ( ptr == 0 ) || ( reinterpret_cast(ptr) == ~uintptr_t(0) ) + || ( reinterpret_cast(ptr) & alignment_mask ) ) { + std::ostringstream msg ; + msg << "Kokkos::Experimental::HBWSpace::allocate[ " ; + switch( m_alloc_mech ) { + case STD_MALLOC: msg << "STD_MALLOC" ; break ; + } + msg << " ]( " << arg_alloc_size << " ) FAILED" ; + if ( ptr == NULL ) { msg << " NULL" ; } + else { msg << " NOT ALIGNED " << ptr ; } + + std::cerr << msg.str() << std::endl ; + std::cerr.flush(); + + Kokkos::Impl::throw_runtime_exception( msg.str() ); + } + + return ptr; +} + + +void HBWSpace::deallocate( void * const arg_alloc_ptr , const size_t arg_alloc_size ) const +{ + if ( arg_alloc_ptr ) { + + if ( m_alloc_mech == STD_MALLOC ) { + void * alloc_ptr = *(reinterpret_cast(arg_alloc_ptr) -1); + memkind_free(MEMKIND_TYPE, alloc_ptr ); + } + + } +} + +} // namespace Experimental +} // namespace Kokkos + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Experimental { +namespace Impl { + +SharedAllocationRecord< void , void > +SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >::s_root_record ; + +void +SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +deallocate( SharedAllocationRecord< void , void > * arg_rec ) +{ + delete static_cast(arg_rec); +} + +SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +~SharedAllocationRecord() +{ + m_space.deallocate( SharedAllocationRecord< void , void >::m_alloc_ptr + , SharedAllocationRecord< void , void >::m_alloc_size + ); +} + +SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +SharedAllocationRecord( const Kokkos::Experimental::HBWSpace & arg_space + , const std::string & arg_label + , const size_t arg_alloc_size + , const SharedAllocationRecord< void , void >::function_type arg_dealloc + ) + // Pass through allocated [ SharedAllocationHeader , user_memory ] + // Pass through deallocation function + : SharedAllocationRecord< void , void > + ( & SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >::s_root_record + , reinterpret_cast( arg_space.allocate( sizeof(SharedAllocationHeader) + arg_alloc_size ) ) + , sizeof(SharedAllocationHeader) + arg_alloc_size + , arg_dealloc + ) + , m_space( arg_space ) +{ + // Fill in the Header information + RecordBase::m_alloc_ptr->m_record = static_cast< SharedAllocationRecord< void , void > * >( this ); + + strncpy( RecordBase::m_alloc_ptr->m_label + , arg_label.c_str() + , SharedAllocationHeader::maximum_label_length + ); +} + +//---------------------------------------------------------------------------- + +void * SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +allocate_tracked( const Kokkos::Experimental::HBWSpace & arg_space + , const std::string & arg_alloc_label + , const size_t arg_alloc_size ) +{ + if ( ! arg_alloc_size ) return (void *) 0 ; + + SharedAllocationRecord * const r = + allocate( arg_space , arg_alloc_label , arg_alloc_size ); + + RecordBase::increment( r ); + + return r->data(); +} + +void SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +deallocate_tracked( void * const arg_alloc_ptr ) +{ + if ( arg_alloc_ptr != 0 ) { + SharedAllocationRecord * const r = get_record( arg_alloc_ptr ); + + RecordBase::decrement( r ); + } +} + +void * SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ) +{ + SharedAllocationRecord * const r_old = get_record( arg_alloc_ptr ); + SharedAllocationRecord * const r_new = allocate( r_old->m_space , r_old->get_label() , arg_alloc_size ); + + Kokkos::Impl::DeepCopy( r_new->data() , r_old->data() + , std::min( r_old->size() , r_new->size() ) ); + + RecordBase::increment( r_new ); + RecordBase::decrement( r_old ); + + return r_new->data(); +} + +SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void > * +SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >::get_record( void * alloc_ptr ) +{ + typedef SharedAllocationHeader Header ; + typedef SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void > RecordHost ; + + SharedAllocationHeader const * const head = alloc_ptr ? Header::get_header( alloc_ptr ) : (SharedAllocationHeader *)0 ; + RecordHost * const record = head ? static_cast< RecordHost * >( head->m_record ) : (RecordHost *) 0 ; + + if ( ! alloc_ptr || record->m_alloc_ptr != head ) { + Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >::get_record ERROR" ) ); + } + + return record ; +} + +// Iterate records to print orphaned memory ... +void SharedAllocationRecord< Kokkos::Experimental::HBWSpace , void >:: +print_records( std::ostream & s , const Kokkos::Experimental::HBWSpace & space , bool detail ) +{ + SharedAllocationRecord< void , void >::print_host_accessible_records( s , "HBWSpace" , & s_root_record , detail ); +} + +} // namespace Impl +} // namespace Experimental +} // namespace Kokkos + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +namespace Kokkos { +namespace Experimental { +namespace { + const unsigned HBW_SPACE_ATOMIC_MASK = 0xFFFF; + const unsigned HBW_SPACE_ATOMIC_XOR_MASK = 0x5A39; + static int HBW_SPACE_ATOMIC_LOCKS[HBW_SPACE_ATOMIC_MASK+1]; +} + +namespace Impl { +void init_lock_array_hbw_space() { + static int is_initialized = 0; + if(! is_initialized) + for(int i = 0; i < static_cast (HBW_SPACE_ATOMIC_MASK+1); i++) + HBW_SPACE_ATOMIC_LOCKS[i] = 0; +} + +bool lock_address_hbw_space(void* ptr) { + return 0 == atomic_compare_exchange( &HBW_SPACE_ATOMIC_LOCKS[ + (( size_t(ptr) >> 2 ) & HBW_SPACE_ATOMIC_MASK) ^ HBW_SPACE_ATOMIC_XOR_MASK] , + 0 , 1); +} + +void unlock_address_hbw_space(void* ptr) { + atomic_exchange( &HBW_SPACE_ATOMIC_LOCKS[ + (( size_t(ptr) >> 2 ) & HBW_SPACE_ATOMIC_MASK) ^ HBW_SPACE_ATOMIC_XOR_MASK] , + 0); +} + +} +} +} +#endif diff --git a/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp b/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp index 69b8ce86d0..851a39a3e4 100644 --- a/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp @@ -41,7 +41,7 @@ //@HEADER */ - +#include #include /*--------------------------------------------------------------------------*/ @@ -56,10 +56,7 @@ /*--------------------------------------------------------------------------*/ -#if ( defined( _POSIX_C_SOURCE ) && _POSIX_C_SOURCE >= 200112L ) || \ - ( defined( _XOPEN_SOURCE ) && _XOPEN_SOURCE >= 600 ) - -#define KOKKOS_POSIX_MEMALIGN_AVAILABLE +#if defined(KOKKOS_POSIX_MEMALIGN_AVAILABLE) #include #include @@ -73,8 +70,9 @@ #endif // mmap flags for huge page tables +// the Cuda driver does not interoperate with MAP_HUGETLB #if defined( KOKKOS_POSIX_MMAP_FLAGS ) - #if defined( MAP_HUGETLB ) + #if defined( MAP_HUGETLB ) && ! defined( KOKKOS_HAVE_CUDA ) #define KOKKOS_POSIX_MMAP_FLAGS_HUGE (KOKKOS_POSIX_MMAP_FLAGS | MAP_HUGETLB ) #else #define KOKKOS_POSIX_MMAP_FLAGS_HUGE KOKKOS_POSIX_MMAP_FLAGS @@ -158,6 +156,8 @@ int HostSpace::in_parallel() /*--------------------------------------------------------------------------*/ +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + namespace Kokkos { Impl::AllocationTracker HostSpace::allocate_and_track( const std::string & label, const size_t size ) @@ -167,6 +167,8 @@ Impl::AllocationTracker HostSpace::allocate_and_track( const std::string & label } // namespace Kokkos +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + /*--------------------------------------------------------------------------*/ namespace Kokkos { @@ -225,13 +227,13 @@ void * HostSpace::allocate( const size_t arg_alloc_size ) const static_assert( sizeof(void*) == sizeof(uintptr_t) , "Error sizeof(void*) != sizeof(uintptr_t)" ); - static_assert( Kokkos::Impl::power_of_two< Kokkos::Impl::MEMORY_ALIGNMENT >::value + static_assert( Kokkos::Impl::is_integral_power_of_two( Kokkos::Impl::MEMORY_ALIGNMENT ) , "Memory alignment must be power of two" ); - constexpr size_t alignment = Kokkos::Impl::MEMORY_ALIGNMENT ; - constexpr size_t alignment_mask = alignment - 1 ; + constexpr uintptr_t alignment = Kokkos::Impl::MEMORY_ALIGNMENT ; + constexpr uintptr_t alignment_mask = alignment - 1 ; - void * ptr = NULL; + void * ptr = 0 ; if ( arg_alloc_size ) { @@ -272,9 +274,9 @@ void * HostSpace::allocate( const size_t arg_alloc_size ) const else if ( m_alloc_mech == POSIX_MMAP ) { constexpr size_t use_huge_pages = (1u << 27); constexpr int prot = PROT_READ | PROT_WRITE ; - const int flags = arg_alloc_size < use_huge_pages - ? KOKKOS_POSIX_MMAP_FLAGS - : KOKKOS_POSIX_MMAP_FLAGS_HUGE ; + const int flags = arg_alloc_size < use_huge_pages + ? KOKKOS_POSIX_MMAP_FLAGS + : KOKKOS_POSIX_MMAP_FLAGS_HUGE ; // read write access to private memory @@ -282,8 +284,8 @@ void * HostSpace::allocate( const size_t arg_alloc_size ) const , arg_alloc_size /* size in bytes */ , prot /* memory protection */ , flags /* visibility of updates */ - , -1 /* file descriptor */ - , 0 /* offset */ + , -1 /* file descriptor */ + , 0 /* offset */ ); /* Associated reallocation: @@ -293,8 +295,24 @@ void * HostSpace::allocate( const size_t arg_alloc_size ) const #endif } - if ( reinterpret_cast(ptr) & alignment_mask ) { - Kokkos::Impl::throw_runtime_exception( "Kokkos::HostSpace aligned allocation failed" ); + if ( ( ptr == 0 ) || ( reinterpret_cast(ptr) == ~uintptr_t(0) ) + || ( reinterpret_cast(ptr) & alignment_mask ) ) { + std::ostringstream msg ; + msg << "Kokkos::HostSpace::allocate[ " ; + switch( m_alloc_mech ) { + case STD_MALLOC: msg << "STD_MALLOC" ; break ; + case POSIX_MEMALIGN: msg << "POSIX_MEMALIGN" ; break ; + case POSIX_MMAP: msg << "POSIX_MMAP" ; break ; + case INTEL_MM_ALLOC: msg << "INTEL_MM_ALLOC" ; break ; + } + msg << " ]( " << arg_alloc_size << " ) FAILED" ; + if ( ptr == NULL ) { msg << " NULL" ; } + else { msg << " NOT ALIGNED " << ptr ; } + + std::cerr << msg.str() << std::endl ; + std::cerr.flush(); + + Kokkos::Impl::throw_runtime_exception( msg.str() ); } return ptr; @@ -333,6 +351,9 @@ void HostSpace::deallocate( void * const arg_alloc_ptr , const size_t arg_alloc_ } // namespace Kokkos +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + namespace Kokkos { namespace Experimental { namespace Impl { @@ -380,16 +401,59 @@ SharedAllocationRecord( const Kokkos::HostSpace & arg_space ); } +//---------------------------------------------------------------------------- + +void * SharedAllocationRecord< Kokkos::HostSpace , void >:: +allocate_tracked( const Kokkos::HostSpace & arg_space + , const std::string & arg_alloc_label + , const size_t arg_alloc_size ) +{ + if ( ! arg_alloc_size ) return (void *) 0 ; + + SharedAllocationRecord * const r = + allocate( arg_space , arg_alloc_label , arg_alloc_size ); + + RecordBase::increment( r ); + + return r->data(); +} + +void SharedAllocationRecord< Kokkos::HostSpace , void >:: +deallocate_tracked( void * const arg_alloc_ptr ) +{ + if ( arg_alloc_ptr != 0 ) { + SharedAllocationRecord * const r = get_record( arg_alloc_ptr ); + + RecordBase::decrement( r ); + } +} + +void * SharedAllocationRecord< Kokkos::HostSpace , void >:: +reallocate_tracked( void * const arg_alloc_ptr + , const size_t arg_alloc_size ) +{ + SharedAllocationRecord * const r_old = get_record( arg_alloc_ptr ); + SharedAllocationRecord * const r_new = allocate( r_old->m_space , r_old->get_label() , arg_alloc_size ); + + Kokkos::Impl::DeepCopy( r_new->data() , r_old->data() + , std::min( r_old->size() , r_new->size() ) ); + + RecordBase::increment( r_new ); + RecordBase::decrement( r_old ); + + return r_new->data(); +} + SharedAllocationRecord< Kokkos::HostSpace , void > * SharedAllocationRecord< Kokkos::HostSpace , void >::get_record( void * alloc_ptr ) { typedef SharedAllocationHeader Header ; typedef SharedAllocationRecord< Kokkos::HostSpace , void > RecordHost ; - SharedAllocationHeader const * const head = Header::get_header( alloc_ptr ); - RecordHost * const record = static_cast< RecordHost * >( head->m_record ); + SharedAllocationHeader const * const head = alloc_ptr ? Header::get_header( alloc_ptr ) : (SharedAllocationHeader *)0 ; + RecordHost * const record = head ? static_cast< RecordHost * >( head->m_record ) : (RecordHost *) 0 ; - if ( record->m_alloc_ptr != head ) { + if ( ! alloc_ptr || record->m_alloc_ptr != head ) { Kokkos::Impl::throw_runtime_exception( std::string("Kokkos::Experimental::Impl::SharedAllocationRecord< Kokkos::HostSpace , void >::get_record ERROR" ) ); } @@ -410,6 +474,54 @@ print_records( std::ostream & s , const Kokkos::HostSpace & space , bool detail /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ +namespace Kokkos { +namespace Experimental { +namespace Impl { + +template< class > +struct ViewOperatorBoundsErrorAbort ; + +template<> +struct ViewOperatorBoundsErrorAbort< Kokkos::HostSpace > { + static void apply( const size_t rank + , const size_t n0 , const size_t n1 + , const size_t n2 , const size_t n3 + , const size_t n4 , const size_t n5 + , const size_t n6 , const size_t n7 + , const size_t i0 , const size_t i1 + , const size_t i2 , const size_t i3 + , const size_t i4 , const size_t i5 + , const size_t i6 , const size_t i7 ); +}; + +void ViewOperatorBoundsErrorAbort< Kokkos::HostSpace >:: +apply( const size_t rank + , const size_t n0 , const size_t n1 + , const size_t n2 , const size_t n3 + , const size_t n4 , const size_t n5 + , const size_t n6 , const size_t n7 + , const size_t i0 , const size_t i1 + , const size_t i2 , const size_t i3 + , const size_t i4 , const size_t i5 + , const size_t i6 , const size_t i7 ) +{ + char buffer[512]; + + snprintf( buffer , sizeof(buffer) + , "View operator bounds error : rank(%lu) dim(%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu) index(%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu)" + , rank , n0 , n1 , n2 , n3 , n4 , n5 , n6 , n7 + , i0 , i1 , i2 , i3 , i4 , i5 , i6 , i7 ); + + Kokkos::Impl::throw_runtime_exception( buffer ); +} + +} // namespace Impl +} // namespace Experimental +} // namespace Kokkos + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + namespace Kokkos { namespace { const unsigned HOST_SPACE_ATOMIC_MASK = 0xFFFF; diff --git a/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.cpp b/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.cpp index a88be37dde..50e45166bd 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.cpp @@ -138,9 +138,21 @@ namespace Kokkos { }; void finalize() { - if(NULL != finalizeProfileLibrary) { - (*finalizeProfileLibrary)(); - } + if(NULL != finalizeProfileLibrary) { + (*finalizeProfileLibrary)(); + + // Set all profile hooks to NULL to prevent + // any additional calls. Once we are told to + // finalize, we mean it + beginForCallee = NULL; + beginScanCallee = NULL; + beginReduceCallee = NULL; + endScanCallee = NULL; + endForCallee = NULL; + endReduceCallee = NULL; + initProfileLibrary = NULL; + finalizeProfileLibrary = NULL; + } }; } } diff --git a/lib/kokkos/core/src/impl/Kokkos_Serial_TaskPolicy.cpp b/lib/kokkos/core/src/impl/Kokkos_Serial_TaskPolicy.cpp index 688f97f42e..5f3e65b327 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Serial_TaskPolicy.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Serial_TaskPolicy.cpp @@ -230,7 +230,7 @@ void Task::assign( Task ** const lhs , Task * rhs , const bool no_throw ) namespace { Task * s_ready = 0 ; -Task * s_denied = reinterpret_cast( ~((unsigned long)0) ); +Task * s_denied = reinterpret_cast( ~((uintptr_t)0) ); } diff --git a/lib/kokkos/core/src/impl/Kokkos_Shape.hpp b/lib/kokkos/core/src/impl/Kokkos_Shape.hpp index dba7301270..9749e0a1ff 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Shape.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Shape.hpp @@ -272,14 +272,14 @@ void assert_shape_bounds( const ShapeType & shape , // Must supply at least as many indices as ranks. // Every index must be within bounds. const bool ok = ShapeType::rank <= arg_rank && - i0 < shape.N0 && - i1 < shape.N1 && - i2 < shape.N2 && - i3 < shape.N3 && - i4 < shape.N4 && - i5 < shape.N5 && - i6 < shape.N6 && - i7 < shape.N7 ; + i0 < size_t(shape.N0) && + i1 < size_t(shape.N1) && + i2 < size_t(shape.N2) && + i3 < size_t(shape.N3) && + i4 < size_t(shape.N4) && + i5 < size_t(shape.N5) && + i6 < size_t(shape.N6) && + i7 < size_t(shape.N7) ; if ( ! ok ) { AssertShapeBoundsAbort< Kokkos::Impl::ActiveExecutionMemorySpace > diff --git a/lib/kokkos/core/src/impl/Kokkos_Synchronic.hpp b/lib/kokkos/core/src/impl/Kokkos_Synchronic.hpp new file mode 100644 index 0000000000..b2aea14df4 --- /dev/null +++ b/lib/kokkos/core/src/impl/Kokkos_Synchronic.hpp @@ -0,0 +1,693 @@ +/* + +Copyright (c) 2014, NVIDIA Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +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 HOLDER 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. + +*/ + +#ifndef KOKKOS_SYNCHRONIC_HPP +#define KOKKOS_SYNCHRONIC_HPP + +#include + +#include +#include +#include +#include +#include + +namespace Kokkos { +namespace Impl { + +enum notify_hint { + notify_all, + notify_one, + notify_none +}; +enum expect_hint { + expect_urgent, + expect_delay +}; + +namespace Details { + +template +bool __synchronic_spin_wait_for_update(S const& arg, T const& nval, int attempts) noexcept { + int i = 0; + for(;i < __SYNCHRONIC_SPIN_RELAX(attempts); ++i) + if(__builtin_expect(arg.load(std::memory_order_relaxed) != nval,1)) + return true; + else + __synchronic_relax(); + for(;i < attempts; ++i) + if(__builtin_expect(arg.load(std::memory_order_relaxed) != nval,1)) + return true; + else + __synchronic_yield(); + return false; +} + +struct __exponential_backoff { + __exponential_backoff(int arg_maximum=512) : maximum(arg_maximum), microseconds(8), x(123456789), y(362436069), z(521288629) { + } + static inline void sleep_for(std::chrono::microseconds const& time) { + auto t = time.count(); + if(__builtin_expect(t > 75,0)) { + portable_sleep(time); + } + else if(__builtin_expect(t > 25,0)) + __synchronic_yield(); + else + __synchronic_relax(); + } + void sleep_for_step() { + sleep_for(step()); + } + std::chrono::microseconds step() { + float const f = ranfu(); + int const t = int(microseconds * f); + if(__builtin_expect(f >= 0.95f,0)) + microseconds = 8; + else + microseconds = (std::min)(microseconds>>1,maximum); + return std::chrono::microseconds(t); + } +private : + int maximum, microseconds, x, y, z; + int xorshf96() { + int t; + x ^= x << 16; x ^= x >> 5; x ^= x << 1; + t = x; x = y; y = z; z = t ^ x ^ y; + return z; + } + float ranfu() { + return (float)(xorshf96()&(~0UL>>1)) / (float)(~0UL>>1); + } +}; + +template +struct __synchronic_base { + +protected: + std::atomic atom; + + void notify(notify_hint = notify_all) noexcept { + } + void notify(notify_hint = notify_all) volatile noexcept { + } + +public : + __synchronic_base() noexcept = default; + constexpr __synchronic_base(T v) noexcept : atom(v) { } + __synchronic_base(const __synchronic_base&) = delete; + ~__synchronic_base() { } + __synchronic_base& operator=(const __synchronic_base&) = delete; + __synchronic_base& operator=(const __synchronic_base&) volatile = delete; + + void expect_update(T val, expect_hint = expect_urgent) const noexcept { + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_A)) + return; + __exponential_backoff b; + while(atom.load(std::memory_order_relaxed) == val) { + __do_backoff(b); + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_B)) + return; + } + } + void expect_update(T val, expect_hint = expect_urgent) const volatile noexcept { + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_A)) + return; + __exponential_backoff b; + while(atom.load(std::memory_order_relaxed) == val) { + __do_backoff(b); + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_B)) + return; + } + } + + template + void expect_update_until(T val, std::chrono::time_point const& then, expect_hint = expect_urgent) const { + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_A)) + return; + __exponential_backoff b; + std::chrono::milliseconds remains = then - std::chrono::high_resolution_clock::now(); + while(remains > std::chrono::milliseconds::zero() && atom.load(std::memory_order_relaxed) == val) { + __do_backoff(b); + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_B)) + return; + remains = then - std::chrono::high_resolution_clock::now(); + } + } + template + void expect_update_until(T val, std::chrono::time_point const& then, expect_hint = expect_urgent) const volatile { + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_A)) + return; + __exponential_backoff b; + std::chrono::milliseconds remains = then - std::chrono::high_resolution_clock::now(); + while(remains > std::chrono::milliseconds::zero() && atom.load(std::memory_order_relaxed) == val) { + __do_backoff(b); + if(__synchronic_spin_wait_for_update(atom, val, __SYNCHRONIC_SPIN_COUNT_B)) + return; + remains = then - std::chrono::high_resolution_clock::now(); + } + } +}; + +#ifdef __SYNCHRONIC_COMPATIBLE +template +struct __synchronic_base::type> { + +public: + std::atomic atom; + + void notify(notify_hint hint = notify_all) noexcept { + if(__builtin_expect(hint == notify_none,1)) + return; + auto const x = count.fetch_add(0,std::memory_order_acq_rel); + if(__builtin_expect(x,0)) { + if(__builtin_expect(hint == notify_all,1)) + __synchronic_wake_all(&atom); + else + __synchronic_wake_one(&atom); + } + } + void notify(notify_hint hint = notify_all) volatile noexcept { + if(__builtin_expect(hint == notify_none,1)) + return; + auto const x = count.fetch_add(0,std::memory_order_acq_rel); + if(__builtin_expect(x,0)) { + if(__builtin_expect(hint == notify_all,1)) + __synchronic_wake_all_volatile(&atom); + else + __synchronic_wake_one_volatile(&atom); + } + } + +public : + __synchronic_base() noexcept : count(0) { } + constexpr __synchronic_base(T v) noexcept : atom(v), count(0) { } + __synchronic_base(const __synchronic_base&) = delete; + ~__synchronic_base() { } + __synchronic_base& operator=(const __synchronic_base&) = delete; + __synchronic_base& operator=(const __synchronic_base&) volatile = delete; + + void expect_update(T val, expect_hint = expect_urgent) const noexcept { + if(__builtin_expect(__synchronic_spin_wait_for_update(atom, val,__SYNCHRONIC_SPIN_COUNT_A),1)) + return; + while(__builtin_expect(atom.load(std::memory_order_relaxed) == val,1)) { + count.fetch_add(1,std::memory_order_release); + __synchronic_wait(&atom,val); + count.fetch_add(-1,std::memory_order_acquire); + } + } + void expect_update(T val, expect_hint = expect_urgent) const volatile noexcept { + if(__builtin_expect(__synchronic_spin_wait_for_update(atom, val,__SYNCHRONIC_SPIN_COUNT_A),1)) + return; + while(__builtin_expect(atom.load(std::memory_order_relaxed) == val,1)) { + count.fetch_add(1,std::memory_order_release); + __synchronic_wait_volatile(&atom,val); + count.fetch_add(-1,std::memory_order_acquire); + } + } + + template + void expect_update_until(T val, std::chrono::time_point const& then, expect_hint = expect_urgent) const { + if(__builtin_expect(__synchronic_spin_wait_for_update(atom, val,__SYNCHRONIC_SPIN_COUNT_A),1)) + return; + std::chrono::milliseconds remains = then - std::chrono::high_resolution_clock::now(); + while(__builtin_expect(remains > std::chrono::milliseconds::zero() && atom.load(std::memory_order_relaxed) == val,1)) { + count.fetch_add(1,std::memory_order_release); + __synchronic_wait_timed(&atom,val,remains); + count.fetch_add(-1,std::memory_order_acquire); + remains = then - std::chrono::high_resolution_clock::now(); + } + } + template + void expect_update_until(T val, std::chrono::time_point const& then, expect_hint = expect_urgent) const volatile { + if(__builtin_expect(__synchronic_spin_wait_for_update(atom, val,__SYNCHRONIC_SPIN_COUNT_A),1)) + return; + std::chrono::milliseconds remains = then - std::chrono::high_resolution_clock::now(); + while(__builtin_expect(remains > std::chrono::milliseconds::zero() && atom.load(std::memory_order_relaxed) == val,1)) { + count.fetch_add(1,std::memory_order_release); + __synchronic_wait_timed_volatile(&atom,val,remains); + count.fetch_add(-1,std::memory_order_acquire); + remains = then - std::chrono::high_resolution_clock::now(); + } + } +private: + mutable std::atomic count; +}; +#endif + +template +struct __synchronic : public __synchronic_base { + + __synchronic() noexcept = default; + constexpr __synchronic(T v) noexcept : __synchronic_base(v) { } + __synchronic(const __synchronic&) = delete; + __synchronic& operator=(const __synchronic&) = delete; + __synchronic& operator=(const __synchronic&) volatile = delete; +}; + +template +struct __synchronic::value>::type> : public __synchronic_base { + + T fetch_add(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_add(v,m); + this->notify(n); + return t; + } + T fetch_add(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_add(v,m); + this->notify(n); + return t; + } + T fetch_sub(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_sub(v,m); + this->notify(n); + return t; + } + T fetch_sub(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_sub(v,m); + this->notify(n); + return t; + } + T fetch_and(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_and(v,m); + this->notify(n); + return t; + } + T fetch_and(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_and(v,m); + this->notify(n); + return t; + } + T fetch_or(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_or(v,m); + this->notify(n); + return t; + } + T fetch_or(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_or(v,m); + this->notify(n); + return t; + } + T fetch_xor(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_xor(v,m); + this->notify(n); + return t; + } + T fetch_xor(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_xor(v,m); + this->notify(n); + return t; + } + + __synchronic() noexcept = default; + constexpr __synchronic(T v) noexcept : __synchronic_base(v) { } + __synchronic(const __synchronic&) = delete; + __synchronic& operator=(const __synchronic&) = delete; + __synchronic& operator=(const __synchronic&) volatile = delete; + + T operator=(T v) volatile noexcept { + auto const t = this->atom = v; + this->notify(); + return t; + } + T operator=(T v) noexcept { + auto const t = this->atom = v; + this->notify(); + return t; + } + T operator++(int) volatile noexcept { + auto const t = ++this->atom; + this->notify(); + return t; + } + T operator++(int) noexcept { + auto const t = ++this->atom; + this->notify(); + return t; + } + T operator--(int) volatile noexcept { + auto const t = --this->atom; + this->notify(); + return t; + } + T operator--(int) noexcept { + auto const t = --this->atom; + this->notify(); + return t; + } + T operator++() volatile noexcept { + auto const t = this->atom++; + this->notify(); + return t; + } + T operator++() noexcept { + auto const t = this->atom++; + this->notify(); + return t; + } + T operator--() volatile noexcept { + auto const t = this->atom--; + this->notify(); + return t; + } + T operator--() noexcept { + auto const t = this->atom--; + this->notify(); + return t; + } + T operator+=(T v) volatile noexcept { + auto const t = this->atom += v; + this->notify(); + return t; + } + T operator+=(T v) noexcept { + auto const t = this->atom += v; + this->notify(); + return t; + } + T operator-=(T v) volatile noexcept { + auto const t = this->atom -= v; + this->notify(); + return t; + } + T operator-=(T v) noexcept { + auto const t = this->atom -= v; + this->notify(); + return t; + } + T operator&=(T v) volatile noexcept { + auto const t = this->atom &= v; + this->notify(); + return t; + } + T operator&=(T v) noexcept { + auto const t = this->atom &= v; + this->notify(); + return t; + } + T operator|=(T v) volatile noexcept { + auto const t = this->atom |= v; + this->notify(); + return t; + } + T operator|=(T v) noexcept { + auto const t = this->atom |= v; + this->notify(); + return t; + } + T operator^=(T v) volatile noexcept { + auto const t = this->atom ^= v; + this->notify(); + return t; + } + T operator^=(T v) noexcept { + auto const t = this->atom ^= v; + this->notify(); + return t; + } +}; + +template +struct __synchronic : public __synchronic_base { + + T* fetch_add(ptrdiff_t v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_add(v,m); + this->notify(n); + return t; + } + T* fetch_add(ptrdiff_t v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_add(v,m); + this->notify(n); + return t; + } + T* fetch_sub(ptrdiff_t v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.fetch_sub(v,m); + this->notify(n); + return t; + } + T* fetch_sub(ptrdiff_t v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.fetch_sub(v,m); + this->notify(n); + return t; + } + + __synchronic() noexcept = default; + constexpr __synchronic(T* v) noexcept : __synchronic_base(v) { } + __synchronic(const __synchronic&) = delete; + __synchronic& operator=(const __synchronic&) = delete; + __synchronic& operator=(const __synchronic&) volatile = delete; + + T* operator=(T* v) volatile noexcept { + auto const t = this->atom = v; + this->notify(); + return t; + } + T* operator=(T* v) noexcept { + auto const t = this->atom = v; + this->notify(); + return t; + } + T* operator++(int) volatile noexcept { + auto const t = ++this->atom; + this->notify(); + return t; + } + T* operator++(int) noexcept { + auto const t = ++this->atom; + this->notify(); + return t; + } + T* operator--(int) volatile noexcept { + auto const t = --this->atom; + this->notify(); + return t; + } + T* operator--(int) noexcept { + auto const t = --this->atom; + this->notify(); + return t; + } + T* operator++() volatile noexcept { + auto const t = this->atom++; + this->notify(); + return t; + } + T* operator++() noexcept { + auto const t = this->atom++; + this->notify(); + return t; + } + T* operator--() volatile noexcept { + auto const t = this->atom--; + this->notify(); + return t; + } + T* operator--() noexcept { + auto const t = this->atom--; + this->notify(); + return t; + } + T* operator+=(ptrdiff_t v) volatile noexcept { + auto const t = this->atom += v; + this->notify(); + return t; + } + T* operator+=(ptrdiff_t v) noexcept { + auto const t = this->atom += v; + this->notify(); + return t; + } + T* operator-=(ptrdiff_t v) volatile noexcept { + auto const t = this->atom -= v; + this->notify(); + return t; + } + T* operator-=(ptrdiff_t v) noexcept { + auto const t = this->atom -= v; + this->notify(); + return t; + } +}; + +} //namespace Details + +template +struct synchronic : public Details::__synchronic { + + bool is_lock_free() const volatile noexcept { return this->atom.is_lock_free(); } + bool is_lock_free() const noexcept { return this->atom.is_lock_free(); } + void store(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + this->atom.store(v,m); + this->notify(n); + } + void store(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + this->atom.store(v,m); + this->notify(n); + } + T load(std::memory_order m = std::memory_order_seq_cst) const volatile noexcept { return this->atom.load(m); } + T load(std::memory_order m = std::memory_order_seq_cst) const noexcept { return this->atom.load(m); } + + operator T() const volatile noexcept { return (T)this->atom; } + operator T() const noexcept { return (T)this->atom; } + + T exchange(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.exchange(v,m); + this->notify(n); + return t; + } + T exchange(T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.exchange(v,m); + this->notify(n); + return t; + } + bool compare_exchange_weak(T& r, T v, std::memory_order m1, std::memory_order m2, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.compare_exchange_weak(r,v,m1,m2); + this->notify(n); + return t; + } + bool compare_exchange_weak(T& r, T v, std::memory_order m1, std::memory_order m2, notify_hint n = notify_all) noexcept { + auto const t = this->atom.compare_exchange_weak(r,v,m1, m2); + this->notify(n); + return t; + } + bool compare_exchange_strong(T& r, T v, std::memory_order m1, std::memory_order m2, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.compare_exchange_strong(r,v,m1,m2); + this->notify(n); + return t; + } + bool compare_exchange_strong(T& r, T v, std::memory_order m1, std::memory_order m2, notify_hint n = notify_all) noexcept { + auto const t = this->atom.compare_exchange_strong(r,v,m1,m2); + this->notify(n); + return t; + } + bool compare_exchange_weak(T& r, T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.compare_exchange_weak(r,v,m); + this->notify(n); + return t; + } + bool compare_exchange_weak(T& r, T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.compare_exchange_weak(r,v,m); + this->notify(n); + return t; + } + bool compare_exchange_strong(T& r, T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) volatile noexcept { + auto const t = this->atom.compare_exchange_strong(r,v,m); + this->notify(n); + return t; + } + bool compare_exchange_strong(T& r, T v, std::memory_order m = std::memory_order_seq_cst, notify_hint n = notify_all) noexcept { + auto const t = this->atom.compare_exchange_strong(r,v,m); + this->notify(n); + return t; + } + + synchronic() noexcept = default; + constexpr synchronic(T val) noexcept : Details::__synchronic(val) { } + synchronic(const synchronic&) = delete; + ~synchronic() { } + synchronic& operator=(const synchronic&) = delete; + synchronic& operator=(const synchronic&) volatile = delete; + T operator=(T val) noexcept { + return Details::__synchronic::operator=(val); + } + T operator=(T val) volatile noexcept { + return Details::__synchronic::operator=(val); + } + + T load_when_not_equal(T val, std::memory_order order = std::memory_order_seq_cst, expect_hint h = expect_urgent) const noexcept { + Details::__synchronic::expect_update(val,h); + return load(order); + } + T load_when_not_equal(T val, std::memory_order order = std::memory_order_seq_cst, expect_hint h = expect_urgent) const volatile noexcept { + Details::__synchronic::expect_update(val,h); + return load(order); + } + T load_when_equal(T val, std::memory_order order = std::memory_order_seq_cst, expect_hint h = expect_urgent) const noexcept { + for(T nval = load(std::memory_order_relaxed); nval != val; nval = load(std::memory_order_relaxed)) + Details::__synchronic::expect_update(nval,h); + return load(order); + } + T load_when_equal(T val, std::memory_order order = std::memory_order_seq_cst, expect_hint h = expect_urgent) const volatile noexcept { + for(T nval = load(std::memory_order_relaxed); nval != val; nval = load(std::memory_order_relaxed)) + expect_update(nval,h); + return load(order); + } + template + void expect_update_for(T val, std::chrono::duration const& delta, expect_hint h = expect_urgent) const { + Details::__synchronic::expect_update_until(val, std::chrono::high_resolution_clock::now() + delta,h); + } + template < class Rep, class Period> + void expect_update_for(T val, std::chrono::duration const& delta, expect_hint h = expect_urgent) const volatile { + Details::__synchronic::expect_update_until(val, std::chrono::high_resolution_clock::now() + delta,h); + } +}; + +#include + +typedef synchronic synchronic_char; +typedef synchronic synchronic_schar; +typedef synchronic synchronic_uchar; +typedef synchronic synchronic_short; +typedef synchronic synchronic_ushort; +typedef synchronic synchronic_int; +typedef synchronic synchronic_uint; +typedef synchronic synchronic_long; +typedef synchronic synchronic_ulong; +typedef synchronic synchronic_llong; +typedef synchronic synchronic_ullong; +//typedef synchronic synchronic_char16_t; +//typedef synchronic synchronic_char32_t; +typedef synchronic synchronic_wchar_t; + +typedef synchronic synchronic_int_least8_t; +typedef synchronic synchronic_uint_least8_t; +typedef synchronic synchronic_int_least16_t; +typedef synchronic synchronic_uint_least16_t; +typedef synchronic synchronic_int_least32_t; +typedef synchronic synchronic_uint_least32_t; +//typedef synchronic synchronic_int_least_64_t; +typedef synchronic synchronic_uint_least64_t; +typedef synchronic synchronic_int_fast8_t; +typedef synchronic synchronic_uint_fast8_t; +typedef synchronic synchronic_int_fast16_t; +typedef synchronic synchronic_uint_fast16_t; +typedef synchronic synchronic_int_fast32_t; +typedef synchronic synchronic_uint_fast32_t; +typedef synchronic synchronic_int_fast64_t; +typedef synchronic synchronic_uint_fast64_t; +typedef synchronic synchronic_intptr_t; +typedef synchronic synchronic_uintptr_t; +typedef synchronic synchronic_size_t; +typedef synchronic synchronic_ptrdiff_t; +typedef synchronic synchronic_intmax_t; +typedef synchronic synchronic_uintmax_t; + +} +} + +#endif //__SYNCHRONIC_H diff --git a/lib/kokkos/core/src/impl/Kokkos_Synchronic_Config.hpp b/lib/kokkos/core/src/impl/Kokkos_Synchronic_Config.hpp new file mode 100644 index 0000000000..0a6dd6e715 --- /dev/null +++ b/lib/kokkos/core/src/impl/Kokkos_Synchronic_Config.hpp @@ -0,0 +1,169 @@ +/* + +Copyright (c) 2014, NVIDIA Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +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 HOLDER 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. + +*/ + +#ifndef KOKKOS_SYNCHRONIC_CONFIG_H +#define KOKKOS_SYNCHRONIC_CONFIG_H + +#include +#include + +namespace Kokkos { +namespace Impl { + +//the default yield function used inside the implementation is the Standard one +#define __synchronic_yield std::this_thread::yield +#define __synchronic_relax __synchronic_yield + +#if defined(_MSC_VER) + //this is a handy GCC optimization that I use inside the implementation + #define __builtin_expect(condition,common) condition + #if _MSC_VER <= 1800 + //using certain keywords that VC++ temporarily doesn't support + #define _ALLOW_KEYWORD_MACROS + #define noexcept + #define constexpr + #endif + //yes, I define multiple assignment operators + #pragma warning(disable:4522) + //I don't understand how Windows is so bad at timing functions, but is OK + //with straight-up yield loops + #define __do_backoff(b) __synchronic_yield() +#else +#define __do_backoff(b) b.sleep_for_step() +#endif + +//certain platforms have efficient support for spin-waiting built into the operating system +#if defined(__linux__) || (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602) +#if defined(_WIN32_WINNT) +#include +#include + //the combination of WaitOnAddress and WakeByAddressAll is supported on Windows 8.1+ + #define __synchronic_wait(x,v) WaitOnAddress((PVOID)x,(PVOID)&v,sizeof(v),-1) + #define __synchronic_wait_timed(x,v,t) WaitOnAddress((PVOID)x,(PVOID)&v,sizeof(v),std::chrono::duration_cast(t).count()) + #define __synchronic_wake_one(x) WakeByAddressSingle((PVOID)x) + #define __synchronic_wake_all(x) WakeByAddressAll((PVOID)x) + #define __synchronic_wait_volatile(x,v) WaitOnAddress((PVOID)x,(PVOID)&v,sizeof(v),-1) + #define __synchronic_wait_timed_volatile(x,v,t) WaitOnAddress((PVOID)x,(PVOID)&v,sizeof(v),std::chrono::duration_cast(t).count()) + #define __synchronic_wake_one_volatile(x) WakeByAddressSingle((PVOID)x) + #define __synchronic_wake_all_volatile(x) WakeByAddressAll((PVOID)x) + #define __SYNCHRONIC_COMPATIBLE(x) (std::is_pod::value && (sizeof(x) <= 8)) + + inline void native_sleep(unsigned long microseconds) + { + // What to do if microseconds is < 1000? + Sleep(microseconds / 1000); + } + + inline void native_yield() + { + SwitchToThread(); + } +#elif defined(__linux__) + #include + #include + #include + #include + #include + #include + #include + #include + template < class Rep, class Period> + inline timespec to_timespec(std::chrono::duration const& delta) { + struct timespec ts; + ts.tv_sec = static_cast(std::chrono::duration_cast(delta).count()); + assert(!ts.tv_sec); + ts.tv_nsec = static_cast(std::chrono::duration_cast(delta).count()); + return ts; + } + inline long futex(void const* addr1, int op, int val1) { + return syscall(SYS_futex, addr1, op, val1, 0, 0, 0); + } + inline long futex(void const* addr1, int op, int val1, struct timespec timeout) { + return syscall(SYS_futex, addr1, op, val1, &timeout, 0, 0); + } + inline void native_sleep(unsigned long microseconds) + { + usleep(microseconds); + } + inline void native_yield() + { + pthread_yield(); + } + + //the combination of SYS_futex(WAIT) and SYS_futex(WAKE) is supported on all recent Linux distributions + #define __synchronic_wait(x,v) futex(x, FUTEX_WAIT_PRIVATE, v) + #define __synchronic_wait_timed(x,v,t) futex(x, FUTEX_WAIT_PRIVATE, v, to_timespec(t)) + #define __synchronic_wake_one(x) futex(x, FUTEX_WAKE_PRIVATE, 1) + #define __synchronic_wake_all(x) futex(x, FUTEX_WAKE_PRIVATE, INT_MAX) + #define __synchronic_wait_volatile(x,v) futex(x, FUTEX_WAIT, v) + #define __synchronic_wait_volatile_timed(x,v,t) futex(x, FUTEX_WAIT, v, to_timespec(t)) + #define __synchronic_wake_one_volatile(x) futex(x, FUTEX_WAKE, 1) + #define __synchronic_wake_all_volatile(x) futex(x, FUTEX_WAKE, INT_MAX) + #define __SYNCHRONIC_COMPATIBLE(x) (std::is_integral::value && (sizeof(x) <= 4)) + + //the yield function on Linux is better replaced by sched_yield, which is tuned for spin-waiting + #undef __synchronic_yield + #define __synchronic_yield sched_yield + + //for extremely short wait times, just let another hyper-thread run + #undef __synchronic_relax + #define __synchronic_relax() asm volatile("rep; nop" ::: "memory") + +#endif +#endif + +#ifdef _GLIBCXX_USE_NANOSLEEP +inline void portable_sleep(std::chrono::microseconds const& time) +{ std::this_thread::sleep_for(time); } +#else +inline void portable_sleep(std::chrono::microseconds const& time) +{ native_sleep(time.count()); } +#endif + +#ifdef _GLIBCXX_USE_SCHED_YIELD +inline void portable_yield() +{ std::this_thread::yield(); } +#else +inline void portable_yield() +{ native_yield(); } +#endif + +//this is the number of times we initially spin, on the first wait attempt +#define __SYNCHRONIC_SPIN_COUNT_A 16 + +//this is how decide to yield instead of just spinning, 'c' is the current trip count +//#define __SYNCHRONIC_SPIN_YIELD(c) true +#define __SYNCHRONIC_SPIN_RELAX(c) (c>>3) + +//this is the number of times we normally spin, on every subsequent wait attempt +#define __SYNCHRONIC_SPIN_COUNT_B 8 + +} +} + +#endif //__SYNCHRONIC_CONFIG_H diff --git a/lib/kokkos/core/src/impl/Kokkos_Synchronic_n3998.hpp b/lib/kokkos/core/src/impl/Kokkos_Synchronic_n3998.hpp new file mode 100644 index 0000000000..facc8d6d8e --- /dev/null +++ b/lib/kokkos/core/src/impl/Kokkos_Synchronic_n3998.hpp @@ -0,0 +1,162 @@ +/* + +Copyright (c) 2014, NVIDIA Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +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 HOLDER 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. + +*/ + +#ifndef KOKKOS_SYNCHRONIC_N3998_HPP +#define KOKKOS_SYNCHRONIC_N3998_HPP + +#include +#include + +/* +In the section below, a synchronization point represents a point at which a +thread may block until a given synchronization condition has been reached or +at which it may notify other threads that a synchronization condition has +been achieved. +*/ +namespace Kokkos { namespace Impl { + + /* + A latch maintains an internal counter that is initialized when the latch + is created. The synchronization condition is reached when the counter is + decremented to 0. Threads may block at a synchronization point waiting + for the condition to be reached. When the condition is reached, any such + blocked threads will be released. + */ + struct latch { + latch(int val) : count(val), released(false) { } + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + ~latch( ) { } + void arrive( ) { + __arrive( ); + } + void arrive_and_wait( ) { + if(!__arrive( )) + wait( ); + } + void wait( ) { + while(!released.load_when_not_equal(false,std::memory_order_acquire)) + ; + } + bool try_wait( ) { + return released.load(std::memory_order_acquire); + } + private: + bool __arrive( ) { + if(count.fetch_add(-1,std::memory_order_release)!=1) + return false; + released.store(true,std::memory_order_release); + return true; + } + std::atomic count; + synchronic released; + }; + + /* + A barrier is created with an initial value representing the number of threads + that can arrive at the synchronization point. When that many threads have + arrived, the synchronization condition is reached and the threads are + released. The barrier will then reset, and may be reused for a new cycle, in + which the same set of threads may arrive again at the synchronization point. + The same set of threads shall arrive at the barrier in each cycle, otherwise + the behaviour is undefined. + */ + struct barrier { + barrier(int val) : expected(val), arrived(0), nexpected(val), epoch(0) { } + barrier(const barrier&) = delete; + barrier& operator=(const barrier&) = delete; + ~barrier() { } + void arrive_and_wait() { + int const myepoch = epoch.load(std::memory_order_relaxed); + if(!__arrive(myepoch)) + while(epoch.load_when_not_equal(myepoch,std::memory_order_acquire) == myepoch) + ; + } + void arrive_and_drop() { + nexpected.fetch_add(-1,std::memory_order_relaxed); + __arrive(epoch.load(std::memory_order_relaxed)); + } + private: + bool __arrive(int const myepoch) { + int const myresult = arrived.fetch_add(1,std::memory_order_acq_rel) + 1; + if(__builtin_expect(myresult == expected,0)) { + expected = nexpected.load(std::memory_order_relaxed); + arrived.store(0,std::memory_order_relaxed); + epoch.store(myepoch+1,std::memory_order_release); + return true; + } + return false; + } + int expected; + std::atomic arrived, nexpected; + synchronic epoch; + }; + + /* + A notifying barrier behaves as a barrier, but is constructed with a callable + completion function that is invoked after all threads have arrived at the + synchronization point, and before the synchronization condition is reached. + The completion may modify the set of threads that arrives at the barrier in + each cycle. + */ + struct notifying_barrier { + template + notifying_barrier(int val, T && f) : expected(val), arrived(0), nexpected(val), epoch(0), completion(std::forward(f)) { } + notifying_barrier(const notifying_barrier&) = delete; + notifying_barrier& operator=(const notifying_barrier&) = delete; + ~notifying_barrier( ) { } + void arrive_and_wait() { + int const myepoch = epoch.load(std::memory_order_relaxed); + if(!__arrive(myepoch)) + while(epoch.load_when_not_equal(myepoch,std::memory_order_acquire) == myepoch) + ; + } + void arrive_and_drop() { + nexpected.fetch_add(-1,std::memory_order_relaxed); + __arrive(epoch.load(std::memory_order_relaxed)); + } + private: + bool __arrive(int const myepoch) { + int const myresult = arrived.fetch_add(1,std::memory_order_acq_rel) + 1; + if(__builtin_expect(myresult == expected,0)) { + int const newexpected = completion(); + expected = newexpected ? newexpected : nexpected.load(std::memory_order_relaxed); + arrived.store(0,std::memory_order_relaxed); + epoch.store(myepoch+1,std::memory_order_release); + return true; + } + return false; + } + int expected; + std::atomic arrived, nexpected; + synchronic epoch; + std::function completion; + }; +}} + +#endif //__N3998_H diff --git a/lib/kokkos/core/src/impl/Kokkos_Tags.hpp b/lib/kokkos/core/src/impl/Kokkos_Tags.hpp index 4885d37376..b7e6ba23a9 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Tags.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Tags.hpp @@ -50,17 +50,6 @@ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -namespace Kokkos { -//---------------------------------------------------------------------------- - -template -struct Device { - typedef ExecutionSpace execution_space; - typedef MemorySpace memory_space; - typedef Device device_type; -}; -} - //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -103,7 +92,26 @@ template< class C > struct is_memory_traits< C , typename Impl::enable_if_type< typename C::memory_traits >::type > : public bool_< Impl::is_same< C , typename C::memory_traits >::value > {}; +} +} +namespace Kokkos { +//---------------------------------------------------------------------------- + +template< class ExecutionSpace , class MemorySpace > +struct Device { + static_assert( Impl::is_execution_space::value + , "Execution space is not valid" ); + static_assert( Impl::is_memory_space::value + , "Memory space is not valid" ); + typedef ExecutionSpace execution_space; + typedef MemorySpace memory_space; + typedef Device device_type; +}; +} + +namespace Kokkos { +namespace Impl { //---------------------------------------------------------------------------- template< class C , class Enable = void > diff --git a/lib/kokkos/core/src/impl/Kokkos_Traits.hpp b/lib/kokkos/core/src/impl/Kokkos_Traits.hpp index 52358842f5..e2e02c3fa1 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Traits.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Traits.hpp @@ -51,6 +51,21 @@ namespace Kokkos { namespace Impl { +//---------------------------------------------------------------------------- +// Help with C++11 variadic argument packs + +template< unsigned I , class ... Args > +struct variadic_type { typedef void type ; }; + +template< class T , class ... Args > +struct variadic_type< 0 , T , Args ... > + { typedef T type ; }; + +template< unsigned I , class T , class ... Args > +struct variadic_type< I , T , Args ... > + { typedef typename variadic_type< I - 1 , Args ... >::type type ; }; + +//---------------------------------------------------------------------------- /* C++11 conformal compile-time type traits utilities. * Prefer to use C++11 when portably available. */ @@ -249,30 +264,51 @@ struct if_ : public if_c {}; template< typename T > struct is_integral : public integral_constant< bool , ( - Impl::is_same< T , char >::value || - Impl::is_same< T , unsigned char >::value || - Impl::is_same< T , short int >::value || - Impl::is_same< T , unsigned short int >::value || - Impl::is_same< T , int >::value || - Impl::is_same< T , unsigned int >::value || - Impl::is_same< T , long int >::value || - Impl::is_same< T , unsigned long int >::value || - Impl::is_same< T , long long int >::value || - Impl::is_same< T , unsigned long long int >::value || + std::is_same< T , char >::value || + std::is_same< T , unsigned char >::value || + std::is_same< T , short int >::value || + std::is_same< T , unsigned short int >::value || + std::is_same< T , int >::value || + std::is_same< T , unsigned int >::value || + std::is_same< T , long int >::value || + std::is_same< T , unsigned long int >::value || + std::is_same< T , long long int >::value || + std::is_same< T , unsigned long long int >::value || - Impl::is_same< T , int8_t >::value || - Impl::is_same< T , int16_t >::value || - Impl::is_same< T , int32_t >::value || - Impl::is_same< T , int64_t >::value || - Impl::is_same< T , uint8_t >::value || - Impl::is_same< T , uint16_t >::value || - Impl::is_same< T , uint32_t >::value || - Impl::is_same< T , uint64_t >::value + std::is_same< T , int8_t >::value || + std::is_same< T , int16_t >::value || + std::is_same< T , int32_t >::value || + std::is_same< T , int64_t >::value || + std::is_same< T , uint8_t >::value || + std::is_same< T , uint16_t >::value || + std::is_same< T , uint32_t >::value || + std::is_same< T , uint64_t >::value )> {}; //---------------------------------------------------------------------------- +// These 'constexpr'functions can be used as +// both regular functions and meta-function. + +/**\brief There exists integral 'k' such that N = 2^k */ +KOKKOS_INLINE_FUNCTION +constexpr bool is_integral_power_of_two( const size_t N ) +{ return ( 0 < N ) && ( 0 == ( N & ( N - 1 ) ) ); } + +/**\brief Return integral 'k' such that N = 2^k, assuming valid. */ +KOKKOS_INLINE_FUNCTION +constexpr unsigned integral_power_of_two_assume_valid( const size_t N ) +{ return N == 1 ? 0 : 1 + integral_power_of_two_assume_valid( N >> 1 ); } + +/**\brief Return integral 'k' such that N = 2^k, if exists. + * If does not exist return ~0u. + */ +KOKKOS_INLINE_FUNCTION +constexpr unsigned integral_power_of_two( const size_t N ) +{ return is_integral_power_of_two(N) ? integral_power_of_two_assume_valid(N) : ~0u ; } + +//---------------------------------------------------------------------------- template < size_t N > struct is_power_of_two diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewDefault.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewDefault.hpp index 8334af3a3c..94c8e13c1d 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewDefault.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewDefault.hpp @@ -84,8 +84,12 @@ struct ViewAssignment< ViewDefault , ViewDefault , void > dst.m_ptr_on_device = ViewDataManagement< ViewTraits >::create_handle( src.m_ptr_on_device, src.m_tracker ); - dst.m_tracker = src.m_tracker ; - + if( dst.is_managed ) + dst.m_tracker = src.m_tracker ; + else { + dst.m_tracker = AllocationTracker(); + dst.m_management.set_unmanaged(); + } } @@ -117,7 +121,7 @@ struct ViewAssignment< ViewDefault , ViewDefault , void > size_t strides[8]; src.stride(strides); if(strides[0]!=1) { - abort("Trying to assign strided 1D View to LayoutRight or LayoutLeft which is not stride-1"); + Kokkos::abort("Trying to assign strided 1D View to LayoutRight or LayoutLeft which is not stride-1"); } dst.m_offset_map.assign( src.dimension_0(), 0, 0, 0, 0, 0, 0, 0, 0 ); @@ -125,8 +129,12 @@ struct ViewAssignment< ViewDefault , ViewDefault , void > dst.m_ptr_on_device = ViewDataManagement< ViewTraits >::create_handle( src.m_ptr_on_device, src.m_tracker ); - dst.m_tracker = src.m_tracker ; - + if( dst.is_managed ) + dst.m_tracker = src.m_tracker ; + else { + dst.m_tracker = AllocationTracker(); + dst.m_management.set_unmanaged(); + } } //------------------------------------ diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewSupport.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewSupport.hpp index 006b35923d..1d54b7bcce 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewSupport.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewSupport.hpp @@ -44,6 +44,7 @@ #ifndef KOKKOS_VIEWSUPPORT_HPP #define KOKKOS_VIEWSUPPORT_HPP +#include #include #include @@ -114,6 +115,7 @@ template< class ExecSpace , class Type , bool Initialize > struct ViewDefaultConstruct { ViewDefaultConstruct( Type * , size_t ) {} }; +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) /** \brief ViewDataHandle provides the type of the 'data handle' which the view * uses to access data with the [] operator. It also provides @@ -240,6 +242,8 @@ public: } }; +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + } // namespace Impl } // namespace Kokkos diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewTileLeft.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewTileLeft.hpp index 0bbb781c87..beaa288ce6 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewTileLeft.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewTileLeft.hpp @@ -79,8 +79,8 @@ struct ViewOffset< ShapeType )>::type > : public ShapeType { - enum { SHIFT_0 = Impl::power_of_two::value }; - enum { SHIFT_1 = Impl::power_of_two::value }; + enum { SHIFT_0 = Impl::integral_power_of_two(N0) }; + enum { SHIFT_1 = Impl::integral_power_of_two(N1) }; enum { MASK_0 = N0 - 1 }; enum { MASK_1 = N1 - 1 }; diff --git a/lib/kokkos/core/src/impl/Kokkos_hwloc.cpp b/lib/kokkos/core/src/impl/Kokkos_hwloc.cpp index 1d173fb4fb..cb561f711c 100644 --- a/lib/kokkos/core/src/impl/Kokkos_hwloc.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_hwloc.cpp @@ -1,13 +1,13 @@ /* //@HEADER // ************************************************************************ -// +// // Kokkos v. 2.0 // Copyright (2014) Sandia Corporation -// +// // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,7 +36,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov) -// +// // ************************************************************************ //@HEADER */ @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -253,6 +254,7 @@ hwloc_topology_t s_hwloc_topology(0); hwloc_bitmap_t s_hwloc_location(0); hwloc_bitmap_t s_process_binding(0); hwloc_bitmap_t s_core[ MAX_CORE ]; +bool s_can_bind_threads(true); struct Sentinel { ~Sentinel(); @@ -309,6 +311,22 @@ Sentinel::Sentinel() hwloc_get_cpubind( s_hwloc_topology , s_process_binding , HWLOC_CPUBIND_PROCESS ); + if ( hwloc_bitmap_iszero( s_process_binding ) ) { + std::cerr << "WARNING: Cannot detect process binding -- ASSUMING ALL processing units" << std::endl; + const int pu_depth = hwloc_get_type_depth( s_hwloc_topology, HWLOC_OBJ_PU ); + int num_pu = 1; + if ( pu_depth != HWLOC_TYPE_DEPTH_UNKNOWN ) { + num_pu = hwloc_get_nbobjs_by_depth( s_hwloc_topology, pu_depth ); + } + else { + std::cerr << "WARNING: Cannot detect number of processing units -- ASSUMING 1 (serial)." << std::endl; + num_pu = 1; + } + hwloc_bitmap_set_range( s_process_binding, 0, num_pu-1); + s_can_bind_threads = false; + } + + if ( remove_core_0 ) { const hwloc_obj_t core = hwloc_get_obj_by_type( s_hwloc_topology , HWLOC_OBJ_CORE , 0 ); @@ -509,6 +527,9 @@ unsigned get_available_cores_per_numa() unsigned get_available_threads_per_core() { sentinel(); return s_core_capacity ; } +bool can_bind_threads() +{ sentinel(); return s_can_bind_threads; } + //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -676,6 +697,7 @@ namespace Kokkos { namespace hwloc { bool available() { return false ; } +bool can_bind_threads() { return false ; } unsigned get_available_numa_count() { return 1 ; } unsigned get_available_cores_per_numa() { return 1 ; } diff --git a/lib/kokkos/core/src/impl/Kokkos_spinwait.cpp b/lib/kokkos/core/src/impl/Kokkos_spinwait.cpp index e16d9c4956..aff7f29f89 100644 --- a/lib/kokkos/core/src/impl/Kokkos_spinwait.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_spinwait.cpp @@ -57,8 +57,15 @@ #elif defined ( KOKKOS_HAVE_WINTHREAD ) #include #define YIELD Sleep(0) +#elif defined ( _WIN32) && defined (_MSC_VER) + /* Windows w/ Visual Studio */ + #define NOMINMAX + #include + #include +#define YIELD YieldProcessor(); #elif defined ( _WIN32 ) - #define YIELD __asm__ __volatile__("pause\n":::"memory") + /* Windows w/ Intel*/ + #define YIELD __asm__ __volatile__("pause\n":::"memory") #else #include #define YIELD sched_yield() diff --git a/lib/kokkos/core/unit_test/CMakeLists.txt b/lib/kokkos/core/unit_test/CMakeLists.txt new file mode 100644 index 0000000000..e835245e25 --- /dev/null +++ b/lib/kokkos/core/unit_test/CMakeLists.txt @@ -0,0 +1,102 @@ +# +# Add test-only library for gtest to be reused by all the subpackages +# + +SET(GTEST_SOURCE_DIR ${${PARENT_PACKAGE_NAME}_SOURCE_DIR}/tpls/gtest) + +INCLUDE_DIRECTORIES(${GTEST_SOURCE_DIR}) +TRIBITS_ADD_LIBRARY( + kokkos_gtest + HEADERS ${GTEST_SOURCE_DIR}/gtest/gtest.h + SOURCES ${GTEST_SOURCE_DIR}/gtest/gtest-all.cc + TESTONLY + ) + +# +# Define the tests +# + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +IF(Kokkos_ENABLE_Serial) + TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_Serial + SOURCES UnitTestMain.cpp TestSerial.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) +ENDIF() + +IF(Kokkos_ENABLE_Pthread) + TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_Threads + SOURCES UnitTestMain.cpp TestThreads.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) +ENDIF() + +IF(Kokkos_ENABLE_OpenMP) + TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_OpenMP + SOURCES UnitTestMain.cpp TestOpenMP.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) +ENDIF() + +IF(Kokkos_ENABLE_QTHREAD) + TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_Qthread + SOURCES UnitTestMain.cpp TestQthread.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) +ENDIF() + +IF(Kokkos_ENABLE_Cuda) + TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_Cuda + SOURCES UnitTestMain.cpp TestCuda.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest + ) +ENDIF() + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_Default + SOURCES UnitTestMain.cpp TestDefaultDeviceType.cpp TestDefaultDeviceTypeInit.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest +) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_HWLOC + SOURCES UnitTestMain.cpp TestHWLOC.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest +) + +TRIBITS_ADD_EXECUTABLE_AND_TEST( + UnitTest_AllocationTracker + SOURCES UnitTestMain.cpp TestAllocationTracker.cpp + COMM serial mpi + NUM_MPI_PROCS 1 + FAIL_REGULAR_EXPRESSION " FAILED " + TESTONLYLIBS kokkos_gtest +) diff --git a/lib/kokkos/core/unit_test/Makefile b/lib/kokkos/core/unit_test/Makefile index b2d3d55066..5c69c4014f 100644 --- a/lib/kokkos/core/unit_test/Makefile +++ b/lib/kokkos/core/unit_test/Makefile @@ -1,18 +1,17 @@ KOKKOS_PATH = ../.. -GTEST_PATH = ../../TPL/gtest +GTEST_PATH = ../../tpls/gtest vpath %.cpp ${KOKKOS_PATH}/core/unit_test TEST_HEADERS = $(wildcard $(KOKKOS_PATH)/core/unit_test/*.hpp) default: build_all echo "End Build" - include $(KOKKOS_PATH)/Makefile.kokkos ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) - CXX = nvcc_wrapper + CXX = $(NVCC_WRAPPER) CXXFLAGS ?= -O3 LINK = $(CXX) LDFLAGS ?= -lpthread @@ -25,8 +24,8 @@ endif KOKKOS_CXXFLAGS += -I$(GTEST_PATH) -I${KOKKOS_PATH}/core/unit_test -TEST_TARGETS = -TARGETS = +TEST_TARGETS = +TARGETS = ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) OBJ_CUDA = TestCuda.o UnitTestMain.o gtest-all.o @@ -74,13 +73,16 @@ OBJ_DEFAULTINIT = TestDefaultDeviceTypeInit.o UnitTestMain.o gtest-all.o TARGETS += KokkosCore_UnitTest_DefaultInit TEST_TARGETS += test-default-init +OBJ_SYNCHRONIC = TestSynchronic.o UnitTestMain.o gtest-all.o +TARGETS += KokkosCore_UnitTest_Synchronic +TEST_TARGETS += test-synchronic KokkosCore_UnitTest_Cuda: $(OBJ_CUDA) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_CUDA) $(KOKKOS_LIBS) $(LIB) -o KokkosCore_UnitTest_Cuda KokkosCore_UnitTest_Threads: $(OBJ_THREADS) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_THREADS) $(KOKKOS_LIBS) $(LIB) -o KokkosCore_UnitTest_Threads - + KokkosCore_UnitTest_OpenMP: $(OBJ_OPENMP) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_OPENMP) $(KOKKOS_LIBS) $(LIB) -o KokkosCore_UnitTest_OpenMP @@ -102,6 +104,9 @@ KokkosCore_UnitTest_Default: $(OBJ_DEFAULT) $(KOKKOS_LINK_DEPENDS) KokkosCore_UnitTest_DefaultInit: $(OBJ_DEFAULTINIT) $(KOKKOS_LINK_DEPENDS) $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_DEFAULTINIT) $(KOKKOS_LIBS) $(LIB) -o KokkosCore_UnitTest_DefaultInit +KokkosCore_UnitTest_Synchronic: $(OBJ_SYNCHRONIC) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LDFLAGS) $(EXTRA_PATH) $(OBJ_SYNCHRONIC) $(KOKKOS_LIBS) $(LIB) -o KokkosCore_UnitTest_Synchronic + test-cuda: KokkosCore_UnitTest_Cuda ./KokkosCore_UnitTest_Cuda @@ -113,27 +118,30 @@ test-openmp: KokkosCore_UnitTest_OpenMP test-serial: KokkosCore_UnitTest_Serial ./KokkosCore_UnitTest_Serial - + test-qthread: KokkosCore_UnitTest_Qthread ./KokkosCore_UnitTest_Qthread test-hwloc: KokkosCore_UnitTest_HWLOC ./KokkosCore_UnitTest_HWLOC - + test-allocationtracker: KokkosCore_UnitTest_AllocationTracker ./KokkosCore_UnitTest_AllocationTracker - + test-default: KokkosCore_UnitTest_Default ./KokkosCore_UnitTest_Default - + test-default-init: KokkosCore_UnitTest_DefaultInit ./KokkosCore_UnitTest_DefaultInit +test-synchronic: KokkosCore_UnitTest_Synchronic + ./KokkosCore_UnitTest_Synchronic + build_all: $(TARGETS) test: $(TEST_TARGETS) - -clean: kokkos-clean + +clean: kokkos-clean rm -f *.o $(TARGETS) # Compilation rules @@ -141,6 +149,6 @@ clean: kokkos-clean %.o:%.cpp $(KOKKOS_CPP_DEPENDS) $(TEST_HEADERS) $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< -gtest-all.o:$(GTEST_PATH)/gtest/gtest-all.cc +gtest-all.o:$(GTEST_PATH)/gtest/gtest-all.cc $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $(GTEST_PATH)/gtest/gtest-all.cc diff --git a/lib/kokkos/core/unit_test/TestAggregate.hpp b/lib/kokkos/core/unit_test/TestAggregate.hpp index b16e17b4cf..c106dfd873 100644 --- a/lib/kokkos/core/unit_test/TestAggregate.hpp +++ b/lib/kokkos/core/unit_test/TestAggregate.hpp @@ -711,16 +711,27 @@ int TestViewAggregate() #else /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ +#include + namespace Test { template< class DeviceType > -int TestViewAggregate() +void TestViewAggregate() { -/* - typedef Kokkos::ViewTraits< Kokkos::Array ** , DeviceType > a32_traits ; + typedef Kokkos::Array value_type ; + + typedef Kokkos::Experimental::Impl:: + ViewDataAnalysis< value_type * , Kokkos::LayoutLeft , value_type > + analysis_1d ; + + static_assert( std::is_same< typename analysis_1d::specialize , Kokkos::Array<> >::value , "" ); + + + typedef Kokkos::ViewTraits< value_type ** , DeviceType > a32_traits ; typedef Kokkos::ViewTraits< typename a32_traits::array_scalar_type , DeviceType > flat_traits ; static_assert( std::is_same< typename a32_traits::specialize , Kokkos::Array<> >::value , "" ); + static_assert( std::is_same< typename a32_traits::value_type , value_type >::value , "" ); static_assert( a32_traits::rank == 2 , "" ); static_assert( a32_traits::rank_dynamic == 2 , "" ); @@ -730,17 +741,23 @@ int TestViewAggregate() static_assert( flat_traits::dimension::N2 == 32 , "" ); - - typedef Kokkos::View< Kokkos::Array ** , DeviceType > a32_type ; + typedef typename a32_type::array_type a32_flat_type ; - + static_assert( std::is_same< typename a32_type::value_type , value_type >::value , "" ); + static_assert( std::is_same< typename a32_type::pointer_type , double * >::value , "" ); static_assert( a32_type::Rank == 2 , "" ); static_assert( a32_flat_type::Rank == 3 , "" ); -*/ - return 0 ; + a32_type x("test",4,5); + a32_flat_type y( x ); + + ASSERT_EQ( x.extent(0) , 4 ); + ASSERT_EQ( x.extent(1) , 5 ); + ASSERT_EQ( y.extent(0) , 4 ); + ASSERT_EQ( y.extent(1) , 5 ); + ASSERT_EQ( y.extent(2) , 32 ); } } diff --git a/lib/kokkos/core/unit_test/TestAllocationTracker.cpp b/lib/kokkos/core/unit_test/TestAllocationTracker.cpp index 371b0ac758..16f13ff1a8 100644 --- a/lib/kokkos/core/unit_test/TestAllocationTracker.cpp +++ b/lib/kokkos/core/unit_test/TestAllocationTracker.cpp @@ -68,6 +68,9 @@ protected: TEST_F( alocation_tracker, simple) { + +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + using namespace Kokkos::Impl; { @@ -111,6 +114,9 @@ TEST_F( alocation_tracker, simple) EXPECT_EQ( 1u, trackers[i].ref_count() ); } } + +#endif /* #if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ + } TEST_F( alocation_tracker, force_leaks) @@ -125,6 +131,9 @@ TEST_F( alocation_tracker, force_leaks) TEST_F( alocation_tracker, disable_reference_counting) { + +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + using namespace Kokkos::Impl; // test ref count and label { @@ -134,12 +143,17 @@ TEST_F( alocation_tracker, disable_reference_counting) trackers[0] = AllocationTracker( MallocAllocator(), 128,"Test"); for (int i=1; i #include +#include #include #include @@ -121,13 +122,11 @@ TEST_F( cuda , memory_space ) TestMemorySpace< Kokkos::Cuda >(); } -TEST_F( cuda, spaces ) +TEST_F( cuda, uvm ) { if ( Kokkos::CudaUVMSpace::available() ) { - Kokkos::Impl::AllocationTracker tracker = Kokkos::CudaUVMSpace::allocate_and_track("uvm_ptr",sizeof(int)); - - int * uvm_ptr = (int*) tracker.alloc_ptr(); + int * uvm_ptr = (int*) Kokkos::kokkos_malloc< Kokkos::CudaUVMSpace >("uvm_ptr",sizeof(int)); *uvm_ptr = 42 ; @@ -137,6 +136,7 @@ TEST_F( cuda, spaces ) EXPECT_EQ( *uvm_ptr, int(2*42) ); + Kokkos::kokkos_free< Kokkos::CudaUVMSpace >(uvm_ptr ); } } @@ -157,6 +157,11 @@ TEST_F( cuda , impl_view_mapping ) TestViewMappingAtomic< Kokkos::Cuda >::run(); } +TEST_F( cuda , view_of_class ) +{ + TestViewMappingClassValue< Kokkos::Cuda >::run(); +} + template< class MemSpace > struct TestViewCudaTexture { @@ -284,6 +289,12 @@ TEST_F( cuda, view_api ) #endif } + +TEST_F( cuda , view_nested_view ) +{ + ::Test::view_nested_view< Kokkos::Cuda >(); +} + TEST_F( cuda, view_subview_auto_1d_left ) { TestViewSubview::test_auto_1d< Kokkos::LayoutLeft,Kokkos::Cuda >(); } @@ -361,6 +372,13 @@ TEST_F( cuda, shared_team ) TestSharedTeam< Kokkos::Cuda >(); } +#if defined (KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA) +TEST_F( cuda, lambda_shared_team ) +{ + TestLambdaSharedTeam< Kokkos::Cuda >(); +} +#endif + TEST_F( cuda, reduce_dynamic ) { TestReduceDynamic< long , Kokkos::Cuda >( 10000000 ); diff --git a/lib/kokkos/core/unit_test/TestDefaultDeviceType.cpp b/lib/kokkos/core/unit_test/TestDefaultDeviceType.cpp index 73e5bf85ae..1f4a2e84d6 100644 --- a/lib/kokkos/core/unit_test/TestDefaultDeviceType.cpp +++ b/lib/kokkos/core/unit_test/TestDefaultDeviceType.cpp @@ -240,7 +240,7 @@ TEST_F( defaultdevicetype , malloc ) int* data2 = (int*) Kokkos::kokkos_malloc(0); ASSERT_TRUE(data2==NULL); - Kokkos::kokkos_free(data); + Kokkos::kokkos_free(data2); } } // namespace test diff --git a/lib/kokkos/core/unit_test/TestOpenMP.cpp b/lib/kokkos/core/unit_test/TestOpenMP.cpp index 7f3a245675..483352d1e4 100644 --- a/lib/kokkos/core/unit_test/TestOpenMP.cpp +++ b/lib/kokkos/core/unit_test/TestOpenMP.cpp @@ -52,6 +52,7 @@ #include #include +#include #include #include @@ -116,6 +117,10 @@ TEST_F( openmp, view_api) { TestViewAPI< double , Kokkos::OpenMP >(); } +TEST_F( openmp , view_nested_view ) +{ + ::Test::view_nested_view< Kokkos::OpenMP >(); +} TEST_F( openmp, view_subview_auto_1d_left ) { TestViewSubview::test_auto_1d< Kokkos::LayoutLeft,Kokkos::OpenMP >(); @@ -208,6 +213,11 @@ TEST_F( openmp, team_shared_request) { TestSharedTeam< Kokkos::OpenMP >(); } +#if defined(KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA) && !defined(KOKKOS_HAVE_CUDA) +TEST_F( openmp, team_lambda_shared_request) { + TestLambdaSharedTeam< Kokkos::OpenMP >(); +} +#endif TEST_F( openmp , atomics ) { diff --git a/lib/kokkos/core/unit_test/TestQthread.cpp b/lib/kokkos/core/unit_test/TestQthread.cpp index ff40536230..edcf7e90ea 100644 --- a/lib/kokkos/core/unit_test/TestQthread.cpp +++ b/lib/kokkos/core/unit_test/TestQthread.cpp @@ -54,6 +54,7 @@ #include #include +#include #include #include @@ -99,6 +100,11 @@ TEST_F( qthread, view_api) { TestViewAPI< double , Kokkos::Qthread >(); } +TEST_F( qthread , view_nested_view ) +{ + ::Test::view_nested_view< Kokkos::Qthread >(); +} + TEST_F( qthread , range_tag ) { TestRange< Kokkos::Qthread >::test_for(1000); diff --git a/lib/kokkos/core/unit_test/TestSerial.cpp b/lib/kokkos/core/unit_test/TestSerial.cpp index 4514492e4d..212a96fdc7 100644 --- a/lib/kokkos/core/unit_test/TestSerial.cpp +++ b/lib/kokkos/core/unit_test/TestSerial.cpp @@ -209,6 +209,12 @@ TEST_F( serial , team_shared_request) { TestSharedTeam< Kokkos::Serial >(); } +#if defined(KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA) && !defined(KOKKOS_HAVE_CUDA) +TEST_F( serial , team_lambda_shared_request) { + TestLambdaSharedTeam< Kokkos::Serial >(); +} +#endif + TEST_F( serial , team_scan ) { TestScanTeam< Kokkos::Serial >( 10 ); diff --git a/lib/kokkos/core/unit_test/TestSharedAlloc.hpp b/lib/kokkos/core/unit_test/TestSharedAlloc.hpp index 060f5f4605..ab5c240ac4 100644 --- a/lib/kokkos/core/unit_test/TestSharedAlloc.hpp +++ b/lib/kokkos/core/unit_test/TestSharedAlloc.hpp @@ -167,27 +167,37 @@ void test_shared_alloc() // Copy destruction function object into the allocation record rec->m_destroy = SharedAllocDestroy( & destroy_count ); + ASSERT_EQ( rec->use_count() , 0 ); + // Start tracking, increments the use count from 0 to 1 - Tracker track( rec ); + Tracker track ; + + track.assign_allocated_record_to_uninitialized( rec ); ASSERT_EQ( rec->use_count() , 1 ); + ASSERT_EQ( track.use_count() , 1 ); // Verify construction / destruction increment for ( size_t i = 0 ; i < N ; ++i ) { ASSERT_EQ( rec->use_count() , 1 ); { - Tracker local_tracker( rec ); + Tracker local_tracker ; + local_tracker.assign_allocated_record_to_uninitialized( rec ); ASSERT_EQ( rec->use_count() , 2 ); + ASSERT_EQ( local_tracker.use_count() , 2 ); } ASSERT_EQ( rec->use_count() , 1 ); + ASSERT_EQ( track.use_count() , 1 ); } Kokkos::parallel_for( range , [=]( size_t i ){ - Tracker local_tracker( rec ); + Tracker local_tracker ; + local_tracker.assign_allocated_record_to_uninitialized( rec ); ASSERT_GT( rec->use_count() , 1 ); }); ASSERT_EQ( rec->use_count() , 1 ); + ASSERT_EQ( track.use_count() , 1 ); // Destruction of 'track' object deallocates the 'rec' and invokes the destroy function object. } diff --git a/lib/kokkos/core/unit_test/TestSynchronic.cpp b/lib/kokkos/core/unit_test/TestSynchronic.cpp new file mode 100644 index 0000000000..9121dc15a1 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestSynchronic.cpp @@ -0,0 +1,448 @@ +/* + +Copyright (c) 2014, NVIDIA Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +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 HOLDER 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. + +*/ + +//#undef _WIN32_WINNT +//#define _WIN32_WINNT 0x0602 + +#if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__APPLE__) + +// Skip for now + +#else + +#include + +#ifdef USEOMP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +//#include
+//#undef __SYNCHRONIC_COMPATIBLE + +#include +#include + +#include "TestSynchronic.hpp" + +// Uncomment to allow test to dump output +//#define VERBOSE_TEST + +namespace Test { + +unsigned next_table[] = + { + 0, 1, 2, 3, //0-3 + 4, 4, 6, 6, //4-7 + 8, 8, 8, 8, //8-11 + 12, 12, 12, 12, //12-15 + 16, 16, 16, 16, //16-19 + 16, 16, 16, 16, //20-23 + 24, 24, 24, 24, //24-27 + 24, 24, 24, 24, //28-31 + 32, 32, 32, 32, //32-35 + 32, 32, 32, 32, //36-39 + 40, 40, 40, 40, //40-43 + 40, 40, 40, 40, //44-47 + 48, 48, 48, 48, //48-51 + 48, 48, 48, 48, //52-55 + 56, 56, 56, 56, //56-59 + 56, 56, 56, 56, //60-63 + }; + +//change this if you want to allow oversubscription of the system, by default only the range {1-(system size)} is tested +#define FOR_GAUNTLET(x) for(unsigned x = (std::min)(std::thread::hardware_concurrency()*8,unsigned(sizeof(next_table)/sizeof(unsigned))); x; x = next_table[x-1]) + +//set this to override the benchmark of barriers to use OMP barriers instead of n3998 std::barrier +//#define USEOMP + +#if defined(__SYNCHRONIC_COMPATIBLE) + #define PREFIX "futex-" +#else + #define PREFIX "backoff-" +#endif + +//this test uses a custom Mersenne twister to eliminate implementation variation +MersenneTwister mt; + +int dummya = 1, dummyb =1; + +int dummy1 = 1; +std::atomic dummy2(1); +std::atomic dummy3(1); + +double time_item(int const count = (int)1E8) { + + clock_t const start = clock(); + + for(int i = 0;i < count; ++i) + mt.integer(); + + clock_t const end = clock(); + double elapsed_seconds = (end - start) / double(CLOCKS_PER_SEC); + + return elapsed_seconds / count; +} +double time_nil(int const count = (int)1E08) { + + clock_t const start = clock(); + + dummy3 = count; + for(int i = 0;i < (int)1E6; ++i) { + if(dummy1) { + // Do some work while holding the lock + int workunits = dummy3;//(int) (mtc.poissonInterval((float)num_items_critical) + 0.5f); + for (int j = 1; j < workunits; j++) + dummy1 &= j; // Do one work unit + dummy2.fetch_add(dummy1,std::memory_order_relaxed); + } + } + + clock_t const end = clock(); + double elapsed_seconds = (end - start) / double(CLOCKS_PER_SEC); + + return elapsed_seconds / count; +} + + +template +void testmutex_inner(mutex_type& m, std::atomic& t,std::atomic& wc,std::atomic& wnc, int const num_iterations, + int const num_items_critical, int const num_items_noncritical, MersenneTwister& mtc, MersenneTwister& mtnc, bool skip) { + + for(int k = 0; k < num_iterations; ++k) { + + if(num_items_noncritical) { + // Do some work without holding the lock + int workunits = num_items_noncritical;//(int) (mtnc.poissonInterval((float)num_items_noncritical) + 0.5f); + for (int i = 1; i < workunits; i++) + mtnc.integer(); // Do one work unit + wnc.fetch_add(workunits,std::memory_order_relaxed); + } + + t.fetch_add(1,std::memory_order_relaxed); + + if(!skip) { + std::unique_lock l(m); + if(num_items_critical) { + // Do some work while holding the lock + int workunits = num_items_critical;//(int) (mtc.poissonInterval((float)num_items_critical) + 0.5f); + for (int i = 1; i < workunits; i++) + mtc.integer(); // Do one work unit + wc.fetch_add(workunits,std::memory_order_relaxed); + } + } + } +} +template +void testmutex_outer(std::map>& results, std::string const& name, double critical_fraction, double critical_duration) { + + std::ostringstream truename; + truename << name << " (f=" << critical_fraction << ",d=" << critical_duration << ")"; + + std::vector& data = results[truename.str()]; + + double const workItemTime = time_item() , + nilTime = time_nil(); + + int const num_items_critical = (critical_duration <= 0 ? 0 : (std::max)( int(critical_duration / workItemTime + 0.5), int(100 * nilTime / workItemTime + 0.5))), + num_items_noncritical = (num_items_critical <= 0 ? 0 : int( ( 1 - critical_fraction ) * num_items_critical / critical_fraction + 0.5 )); + + FOR_GAUNTLET(num_threads) { + + //Kokkos::Impl::portable_sleep(std::chrono::microseconds(2000000)); + + int const num_iterations = (num_items_critical + num_items_noncritical != 0) ? +#ifdef __SYNCHRONIC_JUST_YIELD + int( 1 / ( 8 * workItemTime ) / (num_items_critical + num_items_noncritical) / num_threads + 0.5 ) : +#else + int( 1 / ( 8 * workItemTime ) / (num_items_critical + num_items_noncritical) / num_threads + 0.5 ) : +#endif +#ifdef WIN32 + int( 1 / workItemTime / (20 * num_threads * num_threads) ); +#else + int( 1 / workItemTime / (200 * num_threads * num_threads) ); +#endif + +#ifdef VERBOSE_TEST + std::cerr << "running " << truename.str() << " #" << num_threads << ", " << num_iterations << " * " << num_items_noncritical << "\n" << std::flush; +#endif + + + std::atomic t[2], wc[2], wnc[2]; + + clock_t start[2], end[2]; + for(int pass = 0; pass < 2; ++pass) { + + t[pass] = 0; + wc[pass] = 0; + wnc[pass] = 0; + + srand(num_threads); + std::vector randomsnc(num_threads), + randomsc(num_threads); + + mutex_type m; + + start[pass] = clock(); +#ifdef USEOMP + omp_set_num_threads(num_threads); + std::atomic _j(0); + #pragma omp parallel + { + int const j = _j.fetch_add(1,std::memory_order_relaxed); + testmutex_inner(m, t[pass], wc[pass], wnc[pass], num_iterations, num_items_critical, num_items_noncritical, randomsc[j], randomsnc[j], pass==0); + num_threads = omp_get_num_threads(); + } +#else + std::vector threads(num_threads); + for(unsigned j = 0; j < num_threads; ++j) + threads[j] = new std::thread([&,j](){ + testmutex_inner(m, t[pass], wc[pass], wnc[pass], num_iterations, num_items_critical, num_items_noncritical, randomsc[j], randomsnc[j], pass==0); + } + ); + for(unsigned j = 0; j < num_threads; ++j) { + threads[j]->join(); + delete threads[j]; + } +#endif + end[pass] = clock(); + } + if(t[0] != t[1]) throw std::string("mismatched iteration counts"); + if(wnc[0] != wnc[1]) throw std::string("mismatched work item counts"); + + double elapsed_seconds_0 = (end[0] - start[0]) / double(CLOCKS_PER_SEC), + elapsed_seconds_1 = (end[1] - start[1]) / double(CLOCKS_PER_SEC); + double time = (elapsed_seconds_1 - elapsed_seconds_0 - wc[1]*workItemTime) / num_iterations; + + data.push_back(time); +#ifdef VERBOSE_TEST + std::cerr << truename.str() << " : " << num_threads << "," << elapsed_seconds_1 / num_iterations << " - " << elapsed_seconds_0 / num_iterations << " - " << wc[1]*workItemTime/num_iterations << " = " << time << " \n"; +#endif + } +} + +template +void testbarrier_inner(barrier_type& b, int const num_threads, int const j, std::atomic& t,std::atomic& w, + int const num_iterations_odd, int const num_iterations_even, + int const num_items_noncritical, MersenneTwister& arg_mt, bool skip) { + + for(int k = 0; k < (std::max)(num_iterations_even,num_iterations_odd); ++k) { + + if(k >= (~j & 0x1 ? num_iterations_odd : num_iterations_even )) { + if(!skip) + b.arrive_and_drop(); + break; + } + + if(num_items_noncritical) { + // Do some work without holding the lock + int workunits = (int) (arg_mt.poissonInterval((float)num_items_noncritical) + 0.5f); + for (int i = 1; i < workunits; i++) + arg_mt.integer(); // Do one work unit + w.fetch_add(workunits,std::memory_order_relaxed); + } + + t.fetch_add(1,std::memory_order_relaxed); + + if(!skip) { + int const thiscount = (std::min)(k+1,num_iterations_odd)*((num_threads>>1)+(num_threads&1)) + (std::min)(k+1,num_iterations_even)*(num_threads>>1); + if(t.load(std::memory_order_relaxed) > thiscount) { + std::cerr << "FAILURE: some threads have run ahead of the barrier (" << t.load(std::memory_order_relaxed) << ">" << thiscount << ").\n"; + EXPECT_TRUE(false); + } +#ifdef USEOMP + #pragma omp barrier +#else + b.arrive_and_wait(); +#endif + if(t.load(std::memory_order_relaxed) < thiscount) { + std::cerr << "FAILURE: some threads have fallen behind the barrier (" << t.load(std::memory_order_relaxed) << "<" << thiscount << ").\n"; + EXPECT_TRUE(false); + } + } + } +} +template +void testbarrier_outer(std::map>& results, std::string const& name, double barrier_frequency, double phase_duration, bool randomIterations = false) { + + std::vector& data = results[name]; + + double const workItemTime = time_item(); + int const num_items_noncritical = int( phase_duration / workItemTime + 0.5 ); + + FOR_GAUNTLET(num_threads) { + + int const num_iterations = int( barrier_frequency ); +#ifdef VERBOSE_TEST + std::cerr << "running " << name << " #" << num_threads << ", " << num_iterations << " * " << num_items_noncritical << "\r" << std::flush; +#endif + + srand(num_threads); + + MersenneTwister local_mt; + int const num_iterations_odd = randomIterations ? int(local_mt.poissonInterval((float)num_iterations)+0.5f) : num_iterations, + num_iterations_even = randomIterations ? int(local_mt.poissonInterval((float)num_iterations)+0.5f) : num_iterations; + + std::atomic t[2], w[2]; + std::chrono::time_point start[2], end[2]; + for(int pass = 0; pass < 2; ++pass) { + + t[pass] = 0; + w[pass] = 0; + + srand(num_threads); + std::vector randoms(num_threads); + + barrier_type b(num_threads); + + start[pass] = std::chrono::high_resolution_clock::now(); +#ifdef USEOMP + omp_set_num_threads(num_threads); + std::atomic _j(0); + #pragma omp parallel + { + int const j = _j.fetch_add(1,std::memory_order_relaxed); + testbarrier_inner(b, num_threads, j, t[pass], w[pass], num_iterations_odd, num_iterations_even, num_items_noncritical, randoms[j], pass==0); + num_threads = omp_get_num_threads(); + } +#else + std::vector threads(num_threads); + for(unsigned j = 0; j < num_threads; ++j) + threads[j] = new std::thread([&,j](){ + testbarrier_inner(b, num_threads, j, t[pass], w[pass], num_iterations_odd, num_iterations_even, num_items_noncritical, randoms[j], pass==0); + }); + for(unsigned j = 0; j < num_threads; ++j) { + threads[j]->join(); + delete threads[j]; + } +#endif + end[pass] = std::chrono::high_resolution_clock::now(); + } + + if(t[0] != t[1]) throw std::string("mismatched iteration counts"); + if(w[0] != w[1]) throw std::string("mismatched work item counts"); + + int const phases = (std::max)(num_iterations_odd, num_iterations_even); + + std::chrono::duration elapsed_seconds_0 = end[0]-start[0], + elapsed_seconds_1 = end[1]-start[1]; + double const time = (elapsed_seconds_1.count() - elapsed_seconds_0.count()) / phases; + + data.push_back(time); +#ifdef VERBOSE_TEST + std::cerr << name << " : " << num_threads << "," << elapsed_seconds_1.count() / phases << " - " << elapsed_seconds_0.count() / phases << " = " << time << " \n"; +#endif + } +} + +template +struct mutex_tester; +template +struct mutex_tester { + static void run(std::map>& results, std::string const name[], double critical_fraction, double critical_duration) { + testmutex_outer(results, *name, critical_fraction, critical_duration); + } +}; +template +struct mutex_tester { + static void run(std::map>& results, std::string const name[], double critical_fraction, double critical_duration) { + mutex_tester::run(results, name, critical_fraction, critical_duration); + mutex_tester::run(results, ++name, critical_fraction, critical_duration); + } +}; + +TEST( synchronic, main ) +{ + //warm up + time_item(); + + //measure up +#ifdef VERBOSE_TEST + std::cerr << "measuring work item speed...\r"; + std::cerr << "work item speed is " << time_item() << " per item, nil is " << time_nil() << "\n"; +#endif + try { + + std::pair testpoints[] = { {1, 0}, /*{1E-1, 10E-3}, {5E-1, 2E-6}, {3E-1, 50E-9},*/ }; + for(auto x : testpoints ) { + + std::map> results; + + //testbarrier_outer(results, PREFIX"bar 1khz 100us", 1E3, x.second); + + std::string const names[] = { + PREFIX"tkt", PREFIX"mcs", PREFIX"ttas", PREFIX"std" +#ifdef WIN32 + ,PREFIX"srw" +#endif + }; + + //run --> + + mutex_tester< + ticket_mutex, mcs_mutex, ttas_mutex, std::mutex +#ifdef WIN32 + ,srw_mutex +#endif + >::run(results, names, x.first, x.second); + + //<-- run + +#ifdef VERBOSE_TEST + std::cout << "threads"; + for(auto & i : results) + std::cout << ",\"" << i.first << '\"'; + std::cout << std::endl; + int j = 0; + FOR_GAUNTLET(num_threads) { + std::cout << num_threads; + for(auto & i : results) + std::cout << ',' << i.second[j]; + std::cout << std::endl; + ++j; + } +#endif + } + } + catch(std::string & e) { + std::cerr << "EXCEPTION : " << e << std::endl; + EXPECT_TRUE( false ); + } +} + +} // namespace Test + +#endif diff --git a/lib/kokkos/core/unit_test/TestSynchronic.hpp b/lib/kokkos/core/unit_test/TestSynchronic.hpp new file mode 100644 index 0000000000..d820129e8b --- /dev/null +++ b/lib/kokkos/core/unit_test/TestSynchronic.hpp @@ -0,0 +1,240 @@ +/* + +Copyright (c) 2014, NVIDIA Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +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 HOLDER 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. + +*/ + +#ifndef TEST_SYNCHRONIC_HPP +#define TEST_SYNCHRONIC_HPP + +#include +#include + +namespace Test { + +template +struct dumb_mutex { + + dumb_mutex () : locked(0) { + } + + void lock() { + while(1) { + bool state = false; + if (locked.compare_exchange_weak(state,true,std::memory_order_acquire)) { + break; + } + while (locked.load(std::memory_order_relaxed)) { + if (!truly) { + Kokkos::Impl::portable_yield(); + } + } + } + } + + void unlock() { + locked.store(false,std::memory_order_release); + } + +private : + std::atomic locked; +}; + +#ifdef WIN32 +#include +#include +#include +struct srw_mutex { + + srw_mutex () { + InitializeSRWLock(&_lock); + } + + void lock() { + AcquireSRWLockExclusive(&_lock); + } + void unlock() { + ReleaseSRWLockExclusive(&_lock); + } + +private : + SRWLOCK _lock; +}; +#endif + +struct ttas_mutex { + + ttas_mutex() : locked(false) { + } + + ttas_mutex(const ttas_mutex&) = delete; + ttas_mutex& operator=(const ttas_mutex&) = delete; + + void lock() { + for(int i = 0;; ++i) { + bool state = false; + if(locked.compare_exchange_weak(state,true,std::memory_order_relaxed,Kokkos::Impl::notify_none)) + break; + locked.expect_update(true); + } + std::atomic_thread_fence(std::memory_order_acquire); + } + void unlock() { + locked.store(false,std::memory_order_release); + } + +private : + Kokkos::Impl::synchronic locked; +}; + +struct ticket_mutex { + + ticket_mutex() : active(0), queue(0) { + } + + ticket_mutex(const ticket_mutex&) = delete; + ticket_mutex& operator=(const ticket_mutex&) = delete; + + void lock() { + int const me = queue.fetch_add(1, std::memory_order_relaxed); + while(me != active.load_when_equal(me, std::memory_order_acquire)) + ; + } + + void unlock() { + active.fetch_add(1,std::memory_order_release); + } +private : + Kokkos::Impl::synchronic active; + std::atomic queue; +}; + +struct mcs_mutex { + + mcs_mutex() : head(nullptr) { + } + + mcs_mutex(const mcs_mutex&) = delete; + mcs_mutex& operator=(const mcs_mutex&) = delete; + + struct unique_lock { + + unique_lock(mcs_mutex & arg_m) : m(arg_m), next(nullptr), ready(false) { + + unique_lock * const h = m.head.exchange(this,std::memory_order_acquire); + if(__builtin_expect(h != nullptr,0)) { + h->next.store(this,std::memory_order_seq_cst,Kokkos::Impl::notify_one); + while(!ready.load_when_not_equal(false,std::memory_order_acquire)) + ; + } + } + + unique_lock(const unique_lock&) = delete; + unique_lock& operator=(const unique_lock&) = delete; + + ~unique_lock() { + unique_lock * h = this; + if(__builtin_expect(!m.head.compare_exchange_strong(h,nullptr,std::memory_order_release, std::memory_order_relaxed),0)) { + unique_lock * n = next.load(std::memory_order_relaxed); + while(!n) + n = next.load_when_not_equal(n,std::memory_order_relaxed); + n->ready.store(true,std::memory_order_release,Kokkos::Impl::notify_one); + } + } + + private: + mcs_mutex & m; + Kokkos::Impl::synchronic next; + Kokkos::Impl::synchronic ready; + }; + +private : + std::atomic head; +}; + +} + +namespace std { +template<> +struct unique_lock : Test::mcs_mutex::unique_lock { + unique_lock(Test::mcs_mutex & arg_m) : Test::mcs_mutex::unique_lock(arg_m) { + } + unique_lock(const unique_lock&) = delete; + unique_lock& operator=(const unique_lock&) = delete; +}; + +} + +/* #include */ +#include + +namespace Test { + +//------------------------------------- +// MersenneTwister +//------------------------------------- +#define MT_IA 397 +#define MT_LEN 624 + +class MersenneTwister +{ + volatile unsigned long m_buffer[MT_LEN][64/sizeof(unsigned long)]; + volatile int m_index; + +public: + MersenneTwister() { + for (int i = 0; i < MT_LEN; i++) + m_buffer[i][0] = rand(); + m_index = 0; + for (int i = 0; i < MT_LEN * 100; i++) + integer(); + } + unsigned long integer() { + // Indices + int i = m_index; + int i2 = m_index + 1; if (i2 >= MT_LEN) i2 = 0; // wrap-around + int j = m_index + MT_IA; if (j >= MT_LEN) j -= MT_LEN; // wrap-around + + // Twist + unsigned long s = (m_buffer[i][0] & 0x80000000) | (m_buffer[i2][0] & 0x7fffffff); + unsigned long r = m_buffer[j][0] ^ (s >> 1) ^ ((s & 1) * 0x9908B0DF); + m_buffer[m_index][0] = r; + m_index = i2; + + // Swizzle + r ^= (r >> 11); + r ^= (r << 7) & 0x9d2c5680UL; + r ^= (r << 15) & 0xefc60000UL; + r ^= (r >> 18); + return r; + } + float poissonInterval(float ooLambda) { + return -logf(1.0f - integer() * 2.3283e-10f) * ooLambda; + } +}; + +} // namespace Test + +#endif //TEST_HPP diff --git a/lib/kokkos/core/unit_test/TestTeam.hpp b/lib/kokkos/core/unit_test/TestTeam.hpp index 4849f18dfb..fb001917a3 100644 --- a/lib/kokkos/core/unit_test/TestTeam.hpp +++ b/lib/kokkos/core/unit_test/TestTeam.hpp @@ -461,6 +461,71 @@ struct TestSharedTeam { } }; +#if defined (KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA) + +template< class ExecSpace > +struct TestLambdaSharedTeam { + + TestLambdaSharedTeam() + { run(); } + + void run() + { + typedef Test::SharedTeamFunctor Functor ; + typedef Kokkos::View< typename Functor::value_type , Kokkos::HostSpace , Kokkos::MemoryUnmanaged > result_type ; + typedef typename ExecSpace::scratch_memory_space shmem_space ; + + // tbd: MemoryUnmanaged should be the default for shared memory space + typedef Kokkos::View shared_int_array_type ; + + const int SHARED_COUNT = 1000; + int team_size = 1; +#ifdef KOKKOS_HAVE_CUDA + if(std::is_same::value) + team_size = 128; +#endif + Kokkos::TeamPolicy< ExecSpace > team_exec( 8192 / team_size , team_size , + Kokkos::Experimental::TeamScratchRequest(SHARED_COUNT*2*sizeof(int))); + + typename Functor::value_type error_count = 0 ; + + Kokkos::parallel_reduce( team_exec , KOKKOS_LAMBDA + ( const typename Kokkos::TeamPolicy< ExecSpace >::member_type & ind , int & update ) { + + const shared_int_array_type shared_A( ind.team_shmem() , SHARED_COUNT ); + const shared_int_array_type shared_B( ind.team_shmem() , SHARED_COUNT ); + + if ((shared_A.ptr_on_device () == NULL && SHARED_COUNT > 0) || + (shared_B.ptr_on_device () == NULL && SHARED_COUNT > 0)) { + printf ("Failed to allocate shared memory of size %lu\n", + static_cast (SHARED_COUNT)); + ++update; // failure to allocate is an error + } else { + for ( int i = ind.team_rank() ; i < SHARED_COUNT ; i += ind.team_size() ) { + shared_A[i] = i + ind.league_rank(); + shared_B[i] = 2 * i + ind.league_rank(); + } + + ind.team_barrier(); + + if ( ind.team_rank() + 1 == ind.team_size() ) { + for ( int i = 0 ; i < SHARED_COUNT ; ++i ) { + if ( shared_A[i] != i + ind.league_rank() ) { + ++update ; + } + if ( shared_B[i] != 2 * i + ind.league_rank() ) { + ++update ; + } + } + } + } + }, result_type( & error_count ) ); + + ASSERT_EQ( error_count , 0 ); + } +}; + +#endif } /*--------------------------------------------------------------------------*/ diff --git a/lib/kokkos/core/unit_test/TestThreads.cpp b/lib/kokkos/core/unit_test/TestThreads.cpp index b254aacaf8..772c822552 100644 --- a/lib/kokkos/core/unit_test/TestThreads.cpp +++ b/lib/kokkos/core/unit_test/TestThreads.cpp @@ -60,6 +60,7 @@ #include #include +#include #include #include @@ -133,6 +134,16 @@ TEST_F( threads , init ) { ; } +TEST_F( threads , dispatch ) +{ + const int repeat = 100 ; + for ( int i = 0 ; i < repeat ; ++i ) { + for ( int j = 0 ; j < repeat ; ++j ) { + Kokkos::parallel_for( Kokkos::RangePolicy< Kokkos::Threads >(0,j) + , KOKKOS_LAMBDA( int ) {} ); + }} +} + TEST_F( threads , impl_shared_alloc ) { test_shared_alloc< Kokkos::HostSpace , Kokkos::Threads >(); } @@ -153,6 +164,11 @@ TEST_F( threads, view_api) { TestViewAPI< double , Kokkos::Threads >(); } +TEST_F( threads , view_nested_view ) +{ + ::Test::view_nested_view< Kokkos::Threads >(); +} + TEST_F( threads, view_subview_auto_1d_left ) { TestViewSubview::test_auto_1d< Kokkos::LayoutLeft,Kokkos::Threads >(); } @@ -248,6 +264,12 @@ TEST_F( threads, team_shared_request) { TestSharedTeam< Kokkos::Threads >(); } +#if defined(KOKKOS_HAVE_CXX11_DISPATCH_LAMBDA) && !defined(KOKKOS_HAVE_CUDA) +TEST_F( threads, team_lambda_shared_request) { + TestLambdaSharedTeam< Kokkos::Threads >(); +} +#endif + TEST_F( threads , view_remap ) { enum { N0 = 3 , N1 = 2 , N2 = 8 , N3 = 9 }; diff --git a/lib/kokkos/core/unit_test/TestViewAPI.hpp b/lib/kokkos/core/unit_test/TestViewAPI.hpp index 1aeab1e41c..7b4dac679a 100644 --- a/lib/kokkos/core/unit_test/TestViewAPI.hpp +++ b/lib/kokkos/core/unit_test/TestViewAPI.hpp @@ -1038,9 +1038,15 @@ public: dx = dView4( "dx" , N0 ); dy = dView4( "dy" , N0 ); - + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(1) ); + #endif dView4_unmanaged unmanaged_dx = dx; + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(1) ); + #endif + dView4_unmanaged unmanaged_from_ptr_dx = dView4_unmanaged(dx.ptr_on_device(), dx.dimension_0(), dx.dimension_1(), @@ -1057,6 +1063,36 @@ public: } const_dView4 const_dx = dx ; + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(2) ); + #endif + + { + const_dView4 const_dx2; + const_dx2 = const_dx; + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(3) ); + #endif + + const_dx2 = dy; + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(2) ); + #endif + + const_dView4 const_dx3(dx); + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(3) ); + #endif + + dView4_unmanaged dx4_unmanaged(dx); + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(3) ); + #endif + } + + #ifndef KOKKOS_USING_EXPERIMENTAL_VIEW + ASSERT_EQ( dx.tracker().ref_count() , size_t(2) ); + #endif ASSERT_FALSE( dx.is_null() ); diff --git a/lib/kokkos/core/unit_test/TestViewMapping.hpp b/lib/kokkos/core/unit_test/TestViewMapping.hpp index e380984107..a184b70e61 100644 --- a/lib/kokkos/core/unit_test/TestViewMapping.hpp +++ b/lib/kokkos/core/unit_test/TestViewMapping.hpp @@ -53,17 +53,6 @@ namespace Test { -template< class RangeType > -void test_view_range( const size_t N , const RangeType & range , const size_t begin , const size_t dim ) -{ - typedef Kokkos::Experimental::Impl::ViewOffsetRange< RangeType > query ; - - ASSERT_EQ( query::begin( range ) , begin ); - ASSERT_EQ( query::dimension( N , range ) , dim ); - ASSERT_EQ( query::is_range , dim != 0 ); -} - - template< class ExecSpace > void test_view_mapping() { @@ -397,6 +386,38 @@ void test_view_mapping() //---------------------------------------- // Subview + { + // Mapping rank 4 to rank 3 + typedef Kokkos::Experimental::Impl::SubviewExtents<4,3> SubviewExtents ; + + constexpr int N0 = 1000 ; + constexpr int N1 = 2000 ; + constexpr int N2 = 3000 ; + constexpr int N3 = 4000 ; + + Kokkos::Experimental::Impl::ViewDimension dim ; + + SubviewExtents tmp( dim + , N0 / 2 + , Kokkos::Experimental::ALL + , std::pair( N2 / 4 , 10 + N2 / 4 ) + , Kokkos::pair( N3 / 4 , 20 + N3 / 4 ) + ); + + ASSERT_EQ( tmp.domain_offset(0) , N0 / 2 ); + ASSERT_EQ( tmp.domain_offset(1) , 0 ); + ASSERT_EQ( tmp.domain_offset(2) , N2 / 4 ); + ASSERT_EQ( tmp.domain_offset(3) , N3 / 4 ); + + ASSERT_EQ( tmp.range_index(0) , 1 ); + ASSERT_EQ( tmp.range_index(1) , 2 ); + ASSERT_EQ( tmp.range_index(2) , 3 ); + + ASSERT_EQ( tmp.range_extent(0) , N1 ); + ASSERT_EQ( tmp.range_extent(1) , 10 ); + ASSERT_EQ( tmp.range_extent(2) , 20 ); + } + //---------------------------------------- { constexpr int N0 = 2000 ; constexpr int N1 = 300 ; @@ -409,7 +430,14 @@ void test_view_mapping() left_s0_s0_s4 dyn_off3( std::integral_constant(), N0, N1, 0, 0, 0, 0, 0, 0 ); - stride_s0_s0_s0 stride3( dyn_off3 , sub_N0 , sub_N1 , sub_N2 , 0 , 0 , 0 , 0 , 0 ); + Kokkos::Experimental::Impl::SubviewExtents< 3 , 3 > + sub( dyn_off3.m_dim + , Kokkos::pair(0,sub_N0) + , Kokkos::pair(0,sub_N1) + , Kokkos::pair(0,sub_N2) + ); + + stride_s0_s0_s0 stride3( dyn_off3 , sub ); ASSERT_EQ( stride3.dimension_0() , sub_N0 ); ASSERT_EQ( stride3.dimension_1() , sub_N1 ); @@ -440,7 +468,14 @@ void test_view_mapping() right_s0_s0_s4 dyn_off3( std::integral_constant(), N0, N1, 0, 0, 0, 0, 0, 0 ); - stride_s0_s0_s0 stride3( dyn_off3 , sub_N0 , sub_N1 , sub_N2 , 0 , 0 , 0 , 0 , 0 ); + Kokkos::Experimental::Impl::SubviewExtents< 3 , 3 > + sub( dyn_off3.m_dim + , Kokkos::pair(0,sub_N0) + , Kokkos::pair(0,sub_N1) + , Kokkos::pair(0,sub_N2) + ); + + stride_s0_s0_s0 stride3( dyn_off3 , sub ); ASSERT_EQ( stride3.dimension_0() , sub_N0 ); ASSERT_EQ( stride3.dimension_1() , sub_N1 ); @@ -459,17 +494,16 @@ void test_view_mapping() }}} } - //---------------------------------------- - { - constexpr int N = 1000 ; - - test_view_range( N , N / 2 , N / 2 , 0 ); - test_view_range( N , Kokkos::Experimental::ALL , 0 , N ); - test_view_range( N , std::pair( N / 4 , 10 + N / 4 ) , N / 4 , 10 ); - test_view_range( N , Kokkos::pair( N / 4 , 10 + N / 4 ) , N / 4 , 10 ); - } //---------------------------------------- // view data analysis + { + using namespace Kokkos::Experimental::Impl ; + static_assert( rank_dynamic<>::value == 0 , "" ); + static_assert( rank_dynamic<1>::value == 0 , "" ); + static_assert( rank_dynamic<0>::value == 1 , "" ); + static_assert( rank_dynamic<0,1>::value == 1 , "" ); + static_assert( rank_dynamic<0,0,1>::value == 2 , "" ); + } { using namespace Kokkos::Experimental::Impl ; @@ -491,7 +525,9 @@ void test_view_mapping() static_assert( a_const_int_r5::dimension::rank == 5 , "" ); static_assert( a_const_int_r5::dimension::rank_dynamic == 2 , "" ); + static_assert( std::is_same< typename a_const_int_r5::dimension , ViewDimension<0,0,4,5,6> >::value , "" ); + static_assert( std::is_same< typename a_const_int_r5::non_const_value_type , int >::value , "" ); static_assert( a_int_r5::dimension::rank == 5 , "" ); @@ -510,18 +546,18 @@ void test_view_mapping() static_assert( a_int_r5::dimension::rank == 5 , "" ); static_assert( a_int_r5::dimension::rank_dynamic == 3 , "" ); - static_assert( a_int_r5::dimension::arg_N0 == 0 , "" ); - static_assert( a_int_r5::dimension::arg_N1 == 0 , "" ); - static_assert( a_int_r5::dimension::arg_N2 == 0 , "" ); - static_assert( a_int_r5::dimension::arg_N3 == 3 , "" ); - static_assert( a_int_r5::dimension::arg_N4 == 4 , "" ); + static_assert( a_int_r5::dimension::ArgN0 == 0 , "" ); + static_assert( a_int_r5::dimension::ArgN1 == 0 , "" ); + static_assert( a_int_r5::dimension::ArgN2 == 0 , "" ); + static_assert( a_int_r5::dimension::ArgN3 == 3 , "" ); + static_assert( a_int_r5::dimension::ArgN4 == 4 , "" ); static_assert( std::is_same< typename a_int_r5::non_const_value_type , int >::value , "" ); } { using namespace Kokkos::Experimental::Impl ; - typedef ViewDataAnalysis< const int[] , typename ViewArrayAnalysis::non_const_value_type , void > a_const_int_r1 ; + typedef ViewDataAnalysis< const int[] , void > a_const_int_r1 ; static_assert( std::is_same< typename a_const_int_r1::specialize , void >::value , "" ); static_assert( std::is_same< typename a_const_int_r1::dimension , Kokkos::Experimental::Impl::ViewDimension<0> >::value , "" ); @@ -536,10 +572,12 @@ void test_view_mapping() static_assert( std::is_same< typename a_const_int_r1::non_const_type , int * >::value , "" ); static_assert( std::is_same< typename a_const_int_r1::non_const_value_type , int >::value , "" ); - typedef ViewDataAnalysis< const int**[4] , typename ViewArrayAnalysis< const int **[4] >::non_const_value_type , void > a_const_int_r3 ; + typedef ViewDataAnalysis< const int**[4] , void > a_const_int_r3 ; static_assert( std::is_same< typename a_const_int_r3::specialize , void >::value , "" ); + static_assert( std::is_same< typename a_const_int_r3::dimension , Kokkos::Experimental::Impl::ViewDimension<0,0,4> >::value , "" ); + static_assert( std::is_same< typename a_const_int_r3::type , const int**[4] >::value , "" ); static_assert( std::is_same< typename a_const_int_r3::value_type , const int >::value , "" ); static_assert( std::is_same< typename a_const_int_r3::array_scalar_type , const int**[4] >::value , "" ); @@ -564,8 +602,9 @@ void test_view_mapping() int data[N] ; - T vr1(data,N); - C cr1(vr1); + T vr1(data,N); // view of non-const + C cr1(vr1); // view of const from view of non-const + C cr2( (const int *) data , N ); // Generate static_assert error: // T tmp( cr1 ); @@ -761,42 +800,100 @@ void test_view_mapping() ASSERT_EQ( d.dimension_0() , 5 ); ASSERT_EQ( d.dimension_1() , 6 ); } + +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + /* Only works when experimental view is activated */ + { + typedef Kokkos::Experimental::View V ; + typedef Kokkos::Experimental::View U ; + + + V a("a",10); + + ASSERT_EQ( a.use_count() , 1 ); + + V b = a ; + + ASSERT_EQ( a.use_count() , 2 ); + ASSERT_EQ( b.use_count() , 2 ); + + { + U c = b ; // 'c' is compile-time unmanaged + + ASSERT_EQ( a.use_count() , 2 ); + ASSERT_EQ( b.use_count() , 2 ); + ASSERT_EQ( c.use_count() , 2 ); + + V d = c ; // 'd' is run-time unmanaged + + ASSERT_EQ( a.use_count() , 2 ); + ASSERT_EQ( b.use_count() , 2 ); + ASSERT_EQ( c.use_count() , 2 ); + ASSERT_EQ( d.use_count() , 2 ); + } + + ASSERT_EQ( a.use_count() , 2 ); + ASSERT_EQ( b.use_count() , 2 ); + + b = V(); + + ASSERT_EQ( a.use_count() , 1 ); + ASSERT_EQ( b.use_count() , 0 ); + + Kokkos::parallel_for( + Kokkos::RangePolicy< Kokkos::DefaultHostExecutionSpace >(0,10) , + [=]( int i ){ + // 'a' is captured by copy and the capture mechanism + // converts 'a' to an unmanaged copy. + // When the parallel dispatch accepts a move for the lambda + // this count should become 1 + ASSERT_EQ( a.use_count() , 2 ); + V x = a ; + ASSERT_EQ( a.use_count() , 2 ); + ASSERT_EQ( x.use_count() , 2 ); + }); + } +#endif /* #if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) */ } template< class ExecSpace > struct TestViewMappingSubview { - constexpr static int AN = 10 ; + typedef Kokkos::pair range ; + + enum { AN = 10 }; typedef Kokkos::Experimental::View AT ; - typedef Kokkos::Experimental::Subview< AT , true > AS ; + typedef Kokkos::Experimental::View ACT ; + typedef Kokkos::Experimental::Subview< AT , range > AS ; - constexpr static int BN0 = 10 , BN1 = 11 , BN2 = 12 ; + enum { BN0 = 10 , BN1 = 11 , BN2 = 12 }; typedef Kokkos::Experimental::View BT ; - typedef Kokkos::Experimental::Subview< BT , true , true , true > BS ; + typedef Kokkos::Experimental::Subview< BT , range , range , range > BS ; - constexpr static int CN0 = 10 , CN1 = 11 , CN2 = 12 ; + enum { CN0 = 10 , CN1 = 11 , CN2 = 12 }; typedef Kokkos::Experimental::View CT ; - typedef Kokkos::Experimental::Subview< CT , true , true , true , false , false > CS ; + typedef Kokkos::Experimental::Subview< CT , range , range , range , int , int > CS ; - constexpr static int DN0 = 10 , DN1 = 11 , DN2 = 12 ; - typedef Kokkos::Experimental::View DT ; - typedef Kokkos::Experimental::Subview< DT , false , true , true , true , false > DS ; + enum { DN0 = 10 , DN1 = 11 , DN2 = 12 , DN3 = 13 , DN4 = 14 }; + typedef Kokkos::Experimental::View DT ; + typedef Kokkos::Experimental::Subview< DT , int , range , range , range , int > DS ; typedef Kokkos::Experimental::View DLT ; - typedef Kokkos::Experimental::Subview< DLT , true , false , false , false , false > DLS1 ; + typedef Kokkos::Experimental::Subview< DLT , range , int , int , int , int > DLS1 ; static_assert( DLS1::rank == 1 && std::is_same< typename DLS1::array_layout , Kokkos::LayoutLeft >::value , "Subview layout error for rank 1 subview of left-most range of LayoutLeft" ); typedef Kokkos::Experimental::View DRT ; - typedef Kokkos::Experimental::Subview< DRT , false , false , false , false , true > DRS1 ; + typedef Kokkos::Experimental::Subview< DRT , int , int , int , int , range > DRS1 ; static_assert( DRS1::rank == 1 && std::is_same< typename DRS1::array_layout , Kokkos::LayoutRight >::value , "Subview layout error for rank 1 subview of right-most range of LayoutRight" ); AT Aa ; AS Ab ; + ACT Ac ; BT Ba ; BS Bb ; CT Ca ; @@ -807,6 +904,7 @@ struct TestViewMappingSubview { TestViewMappingSubview() : Aa("Aa",AN) , Ab( Kokkos::Experimental::subview( Aa , std::pair(1,AN-1) ) ) + , Ac( Aa , std::pair(1,AN-1) ) , Ba("Ba",BN0,BN1,BN2) , Bb( Kokkos::Experimental::subview( Ba , std::pair(1,BN0-1) @@ -824,9 +922,9 @@ struct TestViewMappingSubview { , Da("Da",DN0,DN1,DN2) , Db( Kokkos::Experimental::subview( Da , 1 - , std::pair(1,DN0-1) , std::pair(1,DN1-1) , std::pair(1,DN2-1) + , std::pair(1,DN3-1) , 2 ) ) { @@ -836,7 +934,11 @@ struct TestViewMappingSubview { KOKKOS_INLINE_FUNCTION void operator()( const int , long & error_count ) const { + auto Ad = Kokkos::Experimental::subview< Kokkos::MemoryUnmanaged >( Aa , Kokkos::pair(1,AN-1) ); + for ( int i = 1 ; i < AN-1 ; ++i ) if( & Aa[i] != & Ab[i-1] ) ++error_count ; + for ( int i = 1 ; i < AN-1 ; ++i ) if( & Aa[i] != & Ac[i-1] ) ++error_count ; + for ( int i = 1 ; i < AN-1 ; ++i ) if( & Aa[i] != & Ad[i-1] ) ++error_count ; for ( int i2 = 1 ; i2 < BN2-1 ; ++i2 ) { for ( int i1 = 1 ; i1 < BN1-1 ; ++i1 ) { @@ -850,9 +952,9 @@ struct TestViewMappingSubview { if ( & Ca(i0,i1,i2,1,2) != & Cb(i0-1,i1-1,i2-1) ) ++error_count ; }}} - for ( int i2 = 1 ; i2 < DN2-1 ; ++i2 ) { - for ( int i1 = 1 ; i1 < DN1-1 ; ++i1 ) { - for ( int i0 = 1 ; i0 < DN0-1 ; ++i0 ) { + for ( int i2 = 1 ; i2 < DN3-1 ; ++i2 ) { + for ( int i1 = 1 ; i1 < DN2-1 ; ++i1 ) { + for ( int i0 = 1 ; i0 < DN1-1 ; ++i0 ) { if ( & Da(1,i0,i1,i2,2) != & Db(i0-1,i1-1,i2-1) ) ++error_count ; }}} } @@ -861,6 +963,35 @@ struct TestViewMappingSubview { { TestViewMappingSubview self ; + ASSERT_EQ( self.Aa.dimension_0() , AN ); + ASSERT_EQ( self.Ab.dimension_0() , AN - 2 ); + ASSERT_EQ( self.Ac.dimension_0() , AN - 2 ); + ASSERT_EQ( self.Ba.dimension_0() , BN0 ); + ASSERT_EQ( self.Ba.dimension_1() , BN1 ); + ASSERT_EQ( self.Ba.dimension_2() , BN2 ); + ASSERT_EQ( self.Bb.dimension_0() , BN0 - 2 ); + ASSERT_EQ( self.Bb.dimension_1() , BN1 - 2 ); + ASSERT_EQ( self.Bb.dimension_2() , BN2 - 2 ); + + ASSERT_EQ( self.Ca.dimension_0() , CN0 ); + ASSERT_EQ( self.Ca.dimension_1() , CN1 ); + ASSERT_EQ( self.Ca.dimension_2() , CN2 ); + ASSERT_EQ( self.Ca.dimension_3() , 13 ); + ASSERT_EQ( self.Ca.dimension_4() , 14 ); + ASSERT_EQ( self.Cb.dimension_0() , CN0 - 2 ); + ASSERT_EQ( self.Cb.dimension_1() , CN1 - 2 ); + ASSERT_EQ( self.Cb.dimension_2() , CN2 - 2 ); + + ASSERT_EQ( self.Da.dimension_0() , DN0 ); + ASSERT_EQ( self.Da.dimension_1() , DN1 ); + ASSERT_EQ( self.Da.dimension_2() , DN2 ); + ASSERT_EQ( self.Da.dimension_3() , DN3 ); + ASSERT_EQ( self.Da.dimension_4() , DN4 ); + + ASSERT_EQ( self.Db.dimension_0() , DN1 - 2 ); + ASSERT_EQ( self.Db.dimension_1() , DN2 - 2 ); + ASSERT_EQ( self.Db.dimension_2() , DN3 - 2 ); + ASSERT_EQ( self.Da.stride_1() , self.Db.stride_0() ); ASSERT_EQ( self.Da.stride_2() , self.Db.stride_1() ); ASSERT_EQ( self.Da.stride_3() , self.Db.stride_2() ); @@ -1073,6 +1204,51 @@ struct TestViewMappingAtomic { } }; +/*--------------------------------------------------------------------------*/ + +template< class ExecSpace > +struct TestViewMappingClassValue { + + struct ValueType { + KOKKOS_INLINE_FUNCTION + ValueType() + { +#if 0 +#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_CUDA ) + printf("TestViewMappingClassValue construct on Cuda\n"); +#elif defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) + printf("TestViewMappingClassValue construct on Host\n"); +#else + printf("TestViewMappingClassValue construct unknown\n"); +#endif +#endif + } + KOKKOS_INLINE_FUNCTION + ~ValueType() + { +#if 0 +#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_CUDA ) + printf("TestViewMappingClassValue destruct on Cuda\n"); +#elif defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) + printf("TestViewMappingClassValue destruct on Host\n"); +#else + printf("TestViewMappingClassValue destruct unknown\n"); +#endif +#endif + } + }; + + static void run() + { + using namespace Kokkos::Experimental ; + ExecSpace::fence(); + { + View< ValueType , ExecSpace > a("a"); + ExecSpace::fence(); + } + ExecSpace::fence(); + } +}; } /* namespace Test */ diff --git a/lib/kokkos/core/unit_test/TestViewOfClass.hpp b/lib/kokkos/core/unit_test/TestViewOfClass.hpp index 09abacd80d..9fe3fabbd6 100644 --- a/lib/kokkos/core/unit_test/TestViewOfClass.hpp +++ b/lib/kokkos/core/unit_test/TestViewOfClass.hpp @@ -52,51 +52,86 @@ namespace Test { -namespace { -volatile int nested_view_count ; -} - template< class Space > -class NestedView { -private: +struct NestedView { + Kokkos::View member ; public: KOKKOS_INLINE_FUNCTION - NestedView() -#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) - : member("member",2) - { Kokkos::atomic_increment( & nested_view_count ); } -#else - : member(){} -#endif - - ~NestedView() -#if defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST ) - { Kokkos::atomic_decrement( & nested_view_count ); } -#else + NestedView() : member() {} -#endif + KOKKOS_INLINE_FUNCTION + NestedView & operator = ( const Kokkos::View & lhs ) + { + member = lhs ; + if ( member.dimension_0() ) Kokkos::atomic_add( & member(0) , 1 ); + return *this ; + } + + KOKKOS_INLINE_FUNCTION + ~NestedView() + { + if ( member.dimension_0() ) { + Kokkos::atomic_add( & member(0) , -1 ); + } + } +}; + +template< class Space > +struct NestedViewFunctor { + + Kokkos::View< NestedView * , Space > nested ; + Kokkos::View array ; + + NestedViewFunctor( + const Kokkos::View< NestedView * , Space > & arg_nested , + const Kokkos::View & arg_array ) + : nested( arg_nested ) + , array( arg_array ) + {} + + KOKKOS_INLINE_FUNCTION + void operator()( int i ) const + { nested[i] = array ; } }; template< class Space > void view_nested_view() { - ASSERT_EQ( 0 , nested_view_count ); + Kokkos::View tracking("tracking",1); + + typename Kokkos::View::HostMirror + host_tracking = Kokkos::create_mirror( tracking ); + { Kokkos::View< NestedView * , Space > a("a_nested_view",2); - ASSERT_EQ( 2 , nested_view_count ); + + Kokkos::parallel_for( Kokkos::RangePolicy(0,2) , NestedViewFunctor( a , tracking ) ); + Kokkos::deep_copy( host_tracking , tracking ); + ASSERT_EQ( 2 , host_tracking(0) ); + Kokkos::View< NestedView * , Space > b("b_nested_view",2); - ASSERT_EQ( 4 , nested_view_count ); + Kokkos::parallel_for( Kokkos::RangePolicy(0,2) , NestedViewFunctor( b , tracking ) ); + Kokkos::deep_copy( host_tracking , tracking ); + ASSERT_EQ( 4 , host_tracking(0) ); + } - // ASSERT_EQ( 0 , nested_view_count ); + Kokkos::deep_copy( host_tracking , tracking ); + +#if defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + ASSERT_EQ( 0 , host_tracking(0) ); +#endif + } } +#if ! defined( KOKKOS_USING_EXPERIMENTAL_VIEW ) + namespace Kokkos { namespace Impl { @@ -122,5 +157,7 @@ struct ViewDefaultConstruct< ExecSpace , Test::NestedView , true > } // namespace Impl } // namespace Kokkos +#endif + /*--------------------------------------------------------------------------*/ diff --git a/lib/kokkos/core/unit_test/TestViewSubview.hpp b/lib/kokkos/core/unit_test/TestViewSubview.hpp index e0c00d3a8b..39f286e531 100644 --- a/lib/kokkos/core/unit_test/TestViewSubview.hpp +++ b/lib/kokkos/core/unit_test/TestViewSubview.hpp @@ -488,6 +488,7 @@ void test_right_0() Kokkos::View x1 = Kokkos::subview( x_static_8, 0, 1, 2, 3, 0, 1, 2, Kokkos::pair(1,3) ); + ASSERT_TRUE( x1.dimension_0() == 2 ); ASSERT_TRUE( & x1(0) == & x_static_8(0,1,2,3,0,1,2,1) ); ASSERT_TRUE( & x1(1) == & x_static_8(0,1,2,3,0,1,2,2) ); @@ -495,6 +496,8 @@ void test_right_0() Kokkos::subview( x_static_8, 0, 1, 2, Kokkos::pair(1,3) , 0, 1, 2, Kokkos::pair(1,3) ); + ASSERT_TRUE( x2.dimension_0() == 2 ); + ASSERT_TRUE( x2.dimension_1() == 2 ); ASSERT_TRUE( & x2(0,0) == & x_static_8(0,1,2,1,0,1,2,1) ); ASSERT_TRUE( & x2(1,0) == & x_static_8(0,1,2,2,0,1,2,1) ); ASSERT_TRUE( & x2(0,1) == & x_static_8(0,1,2,1,0,1,2,2) ); @@ -505,6 +508,8 @@ void test_right_0() Kokkos::subview( x_static_8, 1, Kokkos::pair(0,2), 2, 3 , Kokkos::pair(0,2), 1, 2, 3 ); + ASSERT_TRUE( sx2.dimension_0() == 2 ); + ASSERT_TRUE( sx2.dimension_1() == 2 ); ASSERT_TRUE( & sx2(0,0) == & x_static_8(1,0,2,3,0,1,2,3) ); ASSERT_TRUE( & sx2(1,0) == & x_static_8(1,1,2,3,0,1,2,3) ); ASSERT_TRUE( & sx2(0,1) == & x_static_8(1,0,2,3,1,1,2,3) ); @@ -517,6 +522,10 @@ void test_right_0() , 2, Kokkos::pair(2,4) /* of [5] */ ); + ASSERT_TRUE( sx4.dimension_0() == 2 ); + ASSERT_TRUE( sx4.dimension_1() == 2 ); + ASSERT_TRUE( sx4.dimension_2() == 2 ); + ASSERT_TRUE( sx4.dimension_3() == 2 ); for ( int i0 = 0 ; i0 < (int) sx4.dimension_0() ; ++i0 ) for ( int i1 = 0 ; i1 < (int) sx4.dimension_1() ; ++i1 ) for ( int i2 = 0 ; i2 < (int) sx4.dimension_2() ; ++i2 ) diff --git a/lib/kokkos/doc/Doxyfile b/lib/kokkos/doc/Doxyfile new file mode 100644 index 0000000000..bc5c7486b2 --- /dev/null +++ b/lib/kokkos/doc/Doxyfile @@ -0,0 +1,127 @@ +# +# Include the global look and feel options +# +@INCLUDE = ../../common/Doxyfile +# +# Package options +# +PROJECT_NAME = "Kokkos Core Kernels Package" +PROJECT_NUMBER = "Version of the Day" +OUTPUT_DIRECTORY = . +OUTPUT_LANGUAGE = English + +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = YES +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +INTERNAL_DOCS = NO +CLASS_DIAGRAMS = YES +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +#JAVADOC_AUTOBRIEF = YES +INHERIT_DOCS = YES +INLINE_INHERITED_MEMB = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = NO +TAB_SIZE = 2 +ENABLED_SECTIONS = +SORT_BRIEF_DOCS = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" + +# +# INPUT: Where to find files that Doxygen should process. ../classic +# has a doc/ subdirectory with its own Doxyfile that points to its own +# files. The other Kokkos subpackages don't currently have their own +# Doxyfile files, so we have to do it manually here. +# +# mfh 26 Sep 2013: I've only added those directories in the Core +# subpackage that constitute the "public interface" of that +# subpackage. Please feel free to include additional subdirectories +# of ../core if you want to generate their documentation as well. +# +# mfh 26 Sep 2013: I've only added the Kokkos subpackages here that I +# think are ready for Doxygen documentation generation. Please feel +# free to amend this list as you see fit. +# + +INPUT = index.doc ../classic ../core/src ../containers/src ../linalg/src +FILE_PATTERNS = *.hpp *.cpp *.cuh *.cu +RECURSIVE = NO +EXCLUDE_PATTERNS = *.x *.o *.out +EXAMPLE_PATH = +EXAMPLE_RECURSIVE = YES +EXAMPLE_PATTERNS = *.cpp *.hpp +IMAGE_PATH = +INPUT_FILTER = +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 4 +IGNORE_PREFIX = +# +# What diagrams are created +# +CLASS_GRAPH = YES +COLLABORATION_GRAPH = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +# +# Preprocessing +# +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS DOXYGEN_USE_ONLY +INCLUDE_PATH = ../src +EXPAND_AS_DEFINED = +# +# Links to other packages +# +TAGFILES = ../../common/tag_files/teuchos.tag=../../../teuchos/doc/html ../../common/tag_files/epetra.tag=../../../epetra/doc/html \ + ../../common/tag_files/belos.tag=../../../belos/doc/html ../../common/tag_files/anasazi.tag=../../../anasazi/doc/html \ + ../../common/tag_files/kokkos.tag=../../../kokkos/doc/html +GENERATE_TAGFILE = ../../common/tag_files/tpetra.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = NO +# +# Environment +# +PERL_PATH = /usr/bin/perl +HAVE_DOT = YES +DOT_PATH = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +# +# What kind of documentation is generated +# +#GENERATE_HTML = YES +#HTML_OUTPUT = html +#HTML_HEADER = includes/header.html +#HTML_FOOTER = includes/footer.html +#HTML_STYLESHEET = includes/stylesheet.css +#HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +DISABLE_INDEX = NO +GENERATE_LATEX = NO +GENERATE_RTF = NO +GENERATE_MAN = NO +GENERATE_XML = NO diff --git a/lib/kokkos/doc/Kokkos_PG.pdf b/lib/kokkos/doc/Kokkos_PG.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3c415698c0d9fec315f317b71db19f2a019b6f6e GIT binary patch literal 1359256 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 EgL=o$W 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 $tBV6C&yDuhMEhyI{&HU(;9p9sV*&fRJXogUA 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

`$Ga=vs7#VnY0%vJjfCq-?t2zLsp-ohI8XIkL8j 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

OLiu>W3+axrdTG8z=e~2Ld~I!s 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�esaWQotiN)MbnT6uXM(Nxg2 z`dzBtn_8==wyZY86kSgzrS+vNH6m{cqe5Pc45e%^`A8*0-mK(hja3t+jNSX^ryHc1 z8T4qPz85FI=G@q&34$nRwFJC_*`8fB`)6W-dsz~VunSlBrM@I19qm>>-n0<}a!>+J zKJ!*EZc|qeX_aENO<24qsnL*v56!s^CLRS@1+t*jt_;fF^**sh7$oa1ck^zF@OP;K zv$43G_;!ucCS;}#SdiQ{^!+M13xQ(39 zoO+W++GD)0E$C!X8|HLhye54A&A{x)6Y{o~U~oA_uW&i4+gA1GUhryv!)MnojUA-; zi=mg4qP{m|Q@LdZ&EMRe?t(UenZHVi*m~0m@19!bcZb~wvhhu|Q4+~QJ>JH*vs)^8 z!qAYQmN6JXZ-1B_o7ZUc|9B=>PIQ>dg;9G{y0A1I+|10??ZcSjKX7zv*>(vB-o3XU zS_tObslNE!(2vh6(}^p$O=UB$pUn2Grr;zDDlqWed@~6asMT;2)PxTHANQ9gX&dkU{lPQ0>YfmvPjrbRgndS|*$|GM5o$ctLnmJM zc9Zb4CSO~2GGusgDNypN`4*1qceRZ-qZdU@A>w)EV7m5j^K6Ez)7;0optiE)+Qhss zLU7Lw9^6-Z@(MzL&P0FsJ6{6;Pzx8*nHvl$|G1P~p6sGE(=u zokAo;fv;Tjtr0V!H14UOdh~9*-ud^JF498no5U9HZQD>tEXFX=;uS<__?>-BY%J_n zo)W)U($~?^dGfNjc)q!@$=l2Nz3D6Sy@7bL8@BU##iiq3a`UHJ7<@@%{a_i~Sr}nHBbw2Y!3Z^?#tBAm=!J)q6z0P}) z^kpqw1GU&8v{7<}I_vOko=sGW0Qv%~N9V|<>(Q{11M@?(IMU4cL_-afz@*v6KlxIB z+OKbj2F%iOi(xayqntK&aPVxXk96&8Cz8+a(wb`S$=9D4oW>JO*gM^`LbOxjI~!`= z_)kg~gr1*$kPCE150$3RHQypCEPBCPQ{w_yS%z;T<~gJZgzAWfbG^IkY8ugU!v1ue zd7C#WCu-()GLG-0*yl^kFW9y7DT<|)z_+(tYa+~moKO$64$Fn10X7C<-Z;GC;u3r6 z;DzRUBuPmcny=w|e;QabcdPQ&vaL6kUb+fvKZ{QA&Fv63;PRsr4FaUd2}FbbVt-3x)m0pRF9~BuBs*rwNkd3eb=hjG z-JJCf_V@7>oei32$9$2`Ql9yj6-y;&rVn3TSAc4=2x8Gx6p>GP25IGdy84Ives%VU z(F)U135Wl8DU6_=LSTiMuH+uAlelZ?6UBfZpXn4Fy*pP$tBtEefG&{T7$qUT~| ze0rnqUN7|3e<&Ku0p!TK-bNE!W-JFEoN?NwdAi*t#m+Cx^n!zjMyMKDzkF5FLY`ueuEikjy2MOE6#INp6|9C!%Bp#l7L?FRZ6f@Pc~zuL@f{LAlxMaHi< z^GA%Hagk5=*P)PWMqGTKNFsmxw*8ohexu!rkY;C z;K&f2cZKRoJFsOmlV(j8xj2MG#uoWVP6fkfy(znbgofer+Iha;M?px2#Ap!CQz%j| zZ9%ibXy4C;i5@m6KesRw-3ux-+zBpDuF4;kl}=Llh_FYUWTT{%_%vKfQgRZRtETl* z&eyFn`~VkMZ~i@xFAAWcl?PeY#J*YWAnY`^?fG9NKq3E zMnN)>(m(`jzG8{0Tcgl>R>3i*sv3v4l1(KZ@F$;>;xINd{XaLR z709@Nm5rUbX%DWa`}GwQfP;abmX@1^MNb&->CFY1d&o3-&P^0CphNkJInRC*k=2-rOBL=wIj~jbh2awL8afVg>z~X3uV2jvn#0a`1 zy*Vi8>(_*YR~jqp>)XrAM)sC(JGYZlh&j9&3|Uw1gkGVp;2q+cnMWtY;yxou{gRTZ zs~wp0;R9>O$B=stoEI4VJ>9dj=;K^`Ts$1SV9_*R?uQj%@Og~=GAfpDjI1E$q}B87 z=T>wKW#>3Oe#H4s4glzElC%3M?&|X~pc6Bld{dg*I zZtg%r##fAkQn)O5{k>nNNr+)yo|a=?ZpX&H*L3%2Yfv@`i2^K=-3(-@oC13zKy0J2 z^E*>MR2l3JQ@EVBMw1nV_q8vVm_d-R1f3*1SV~3sJL*ROV-ZZobCmoOLP(f>Bm1+K zMgp6Pi9|3MH)559oZR1xVMi#Ent_r!%n0s&Jl2V-d9<=AW*j+Fzbex}X%Bf%JpNt| zH$W8wD)4DvC~_629}=PJo3E5^mUp{`Biq@-f5^U0_27I$%YQ<<(-^_Pz?Lif=K5GR zBV%)Sj#*MEjKM<1>`m^Gcz`{9=yLqZgH)(dPp(_=>_T?WBdT++WNzSQU`xW6K;@`( zovR?VDfs#=jmU~$C0qS~>XlMXqoW79&f^oUL!t1KkycRB8PD_oyY}-hQV=ib5tXaK zHSw=hv%mHL{!@5qo_~`*Ik*ub?|-f)c%Wx#N+^rZ^TjMWfZ!#AEMY7RY)-2J1A!p{ z(Y6l!nMV(ghIN+)F6U>1!z`5&Z*DuMP;FmmmhK)FJ#408JJXKF!DKsP^%5hS-)>0nsjucR&jh_+Cw&5IhHlT<6KJ?_RPTAoa6mB6GB`$=<83p4 zf2E6I{%J-%x^=Eny`!7k>p|aaNj{wR9&M8DjUi_5NdaauMx|o0&-Ieopiuo52!`&C zfnT8P6uPfVU>e#V2PADhH}9A|!J;7+j?`1}5)E?Nq6MO5J?#sP79}jts|&|GDULZ< zzaII5YTbPkfV-!O_&O=q9 zo9&qjSR7DV@ol>0nl1-Pv_IZ)FjhP(S@;o15&Ly2;-10*%|9%P=z|F{OyoiT%eukU z3zT=>&;}H5KSPMaX5o$8ex8DunZu43KIVsHBz6-X%~q$(6@@X?oOl4ioWu*O&Bg9xS9aAQX|Tdt!{;c4`y=Y)0b$q zc@xPszhB!N()C9q$IRFHVx7t0z#;yFuhH2z2jgCy!idMvmU!g-iAmJwk!X9^81}D3 zT{pQJ=}Xqhi@F;lHO0}oQNd48Y2or(8 zj`SFl$ii;R!o=L%9R2peLH&Y0H2{3z2pJ5zWl)m*veKv+*AJ3-^&ZJ836=j85p8`j z+9Y?dcSK3dO}u?{v{!$yo`R8OC$2d-Ta71?Opunb3hrKP!KB2!xLCv{UI7jQZLtG} zn*|spG8ma?To3`&T}JNi4q22>mqF#ky-;mnaiYlT*jC?Nn~@a1urN121dUiG*uM)f zs}7dh{3()7NQG0xr*}CXYyM5h36Z1$7JLa3^;l1p;%aa5uWpii4<6%@KPT zHBa45d?yI;VqzJF&_OZZyY2S*=Hwm5U_gxa0s{(FAiBz!^W2yG31y{)^e6ou*=d=6 zn0SK=VXx?qqCY^3I&x=mL4DpWZ6hH~*?8^@zs{ENv<>y%yg^}MWqrW{13+EhQ29R{ z4a*G?RzWT%Qsv?ZXxn){S|5hRK#5Op)?Z*l#Xb-~j|ZYTt>|wIz8ZwjFG!Xp?PhO2khah&nxCy7VW&2pIM* z@_cedH7DJeX{cwQuZw!5J2y9iJg1vVIx>}6%+iTu1*~KbC`1c4@_vV@W);lwIjZ3O zCy_p3jn*IN0d)KUXEW;15ETL(hTyifHz={!MX^2ah$J#JaE20ww}cwmFkRg;?CoCy zFoANQ_%`mM_Lq}E=501{JQMpqp4kTI#V?8VMLz2&P z&X`!s?{MwqEeVQh+ReV(0j;tN2x3CuUC~&HW!{ct1eANj0EPzYOa?t+=i+)Bg2V;k z#>pksMH|^+}SxlkMsl^Bl6-bujGJwTni>&*nmptXhc*S_|cfY zD>>{p>MtiA{378ISE_Z@^BjWWqoY=?X+&F&TyBhC{syq2^1QrBMLd=1IWtJ;{cr?wt9|3x#t`53`5s!vU?V(}l*M%cCW~D>%>( zXYrubM-pRNmC!p<7{IJ;EUke&Fu+0@E0n2279gMrRmAd__MZ}`LX|-(PtT%(Q`V1Q ziWP}cp1iiYRB7H8c7I%f!{M?33MdEy==Z!1RCOSTSl$s-cjGCpuyOx(XZ%8WGk{P# zf>4`$XJ8v>2Y8K4x7Su)Kd1?qVT`gvtoio(zB%=tXOH%2P)bl7_>OD~-yN^@#E1dm zP!mS~-;sfIfkRl6NWaPhiUJDNIX+n^Z_=n4g+N5*ilUh}fpW|`Pl&N#LgIp0HtCO` z%L2IZ)tf2VUtjxAV`Urt8vWo8aNOd@yVH?Y032q5GlXme5(keU*8R0bfXo0V3xS`b zwZnJHB(EV;U|4OxFjODK9er6wZn>4`HopWu`q?)|kS$k8cPdOGY@^^u`po@<5JN=M z<4IotHMGP5hytVog)je%(r;5@Tt@PKh%ZdIHWjmK3D=avS!@nbtp~D$ZAh}rachz) z@JvX>86P#j8F-5^E&UL2JtaP_E(9J{)O5_6BuUkbgt0ORT7>*Yf9H)?KCL zNT${Y%6iM>o(;vI-FD#}@KLM4-3>D>JgOeR*$$KjkYrH_M?@?Wpzff|MZ-gPnkZL;}Ks z#ss8fcH;Cy2nQK$UZGFf!V@iil0lKCXDsl#%yG$#POy;x*RsgHpjcg~F-CFL)=3h{ zEw|5>uLG^a$d{gHZdxH8n#NIZOeW(H zpF^wiP|BOa&5f2RFGik^ZgkzfEy|qFv?yWTFl?E1xwqisb1}M}yzjtg@}8=rnnV62 zDQosH0&&L!w?SceDkxRK7FlI?9e3Fdvc(=%e(0>i%MFzio&4^O%zz@LBt<_Lfa zA6)-i%o6@4&FmvC>j4u7K)S62YmM?jj6;+&_z7T0OkK>$ufBR~iDyoBI)6*y8q@3R zTKKh7%x2Q-sV7=CeiL#?{Q(= z{z%fWN&h`%qeq-Wnui!HoQrcZ#ryo6gT^iOYg2-W%1gY{mCGt`>AOzKGN+K2BJh|J| z5u0|Kvz%`ARqs#Q5i`*C{n29V`*`THyx5e%L>>3YgWH@-*ey?=MG0oV=zk0?o5mrQ z3#M+rH54XN^MpA75XHxUjLfYmR{R9>A7XF!@z&`K5aag~?M-SH+=x?F5UGCvR4xwQhyA!O+3LMgd7x5g z?iG2*jepg8#UdK9Uj?Uqs9Q(74&zI{eY_)>N>q6qSU6C8BNnu@eg%(y4ucp^#3*(yPaE{5Y%eEad~ zjmpI`EP==V#c}rfd68^{^6ujuGr>}8$@WV~RsK5FT7`AXfDf@B4HeojC&Jzo%;}brT*X~ntH4>WkV>mqeYAL zGFu$9ngJ0xpNLox5v9u`TNK?%xCynE+eyU^kt%jUPSPd`U~ z_MmEaV$)7}y)_m()x_lIJw|F3J-Fp6^e$7-U@>6h9yQSuw!g^j5Tx9MG8yipWdKn@Q?JKNK}xiCXke|oD^4Tn^Joq{yIW^70a z_43Zs8t1sJSZr?ZgYH77X&+V0MFYlMvrJK#hxMZS@4hM}PxmQxEMd@!c-Y4(YcRhU zq;Vnim(*_KU8(2($&X)DWT)i|TyvXSHf4FHAFb|*@}}gwE_I zmDVY|WKW_`7u=pANg{vGlVMVmwxLK1OBx(y8IOV zzI#=i`jg$Q6<_{MlRMMX7vxZpWh_~d9qU}%gQ)BEV0F|qw(kL9nSCVmO5UQo%abGg z5s6_kJ3yQ{Z;2!fA(7_3=PNDJ8$^Rk*OtG1v%jLDWD)v5j~#ZOsI-dft90k7zq)q) zwc^TuTtj2m4^2CnRm~h0`GR*asYi|-a=IX}e-tWeDAlZSx!PjUXPF0R5))ajl^OPS zl5GbNf;ua5pP!9}`)_wWVB5ouoH{L@yt6H29KR)X?kZcvHkZu)DL#`OB20cji=~v^ zM`RcCCaK3ua0&h668>fKjbu;|ovU*+xTlC<&`^Gef2+^*P$W^MI zTM{X3-*K#1dN5!lkLBpC84c{^kcA{hmW5p-OK6BtT^f+iM(? zWuAk%%myrJ%D-tjSB|& zEvOz^_$JLro+@CRE{j}LRwmsmp)K21Rek-6n5FZQ1dZd1e4W}0`^3ijs)mH!Sqeft}OO64mmuUkM>b;;&W#Y|zqDRJw#aM(8z zneYBcSiBWX zr}!}lgbygNnKpUGtt(k1iMJVpsIZw$BO~xfHPX;{R_7u7#!A<*ME%09jzqEztrF+b zI7VRp5t2RU6wB}de{03lLDP>4_tUQ@jP@Z)kJ?U_&({0slHXCZUJ(%}rTpF`O9Hd! zS8E0))F<$t7O~-Y$jZAVFIbMu1NtSQmWuT_ZS~BG=_SP`pKM*82xKUY2)y7d8(U-V z*{l^Jr1bLSE*ar~vv>B6zlmVasp`x6vMbpuG^6^O$lv8MjBJ{}ujiO%h~oz$X|!}$yB|W1bAJB9#Ch}7Mg(tsRMvLJJ)6Gc>?f3NxXnM8Z#h^g6HW?Dc$y^Y@5FMPym8Q?Jp5BFN z$pNC3hKYh5*FURkv*-g-_;}@cI)`;J-Q!Th>X@@IxCZ&0?E}C@LB;YxB;7OYg!46M z1~?!ABp77!fGK5ViW5ME3mOGC%1mpnz!aYuPGO-~aiJWg7`EzxzS#V(79(5zjLcAE zL3~6a^D$_4{bR|&IMYa~R;%cu&_v(Y)67tqE~+C*8j|r{~4`_YovynYFM|v90JU>jSW;a3$}wA(DWvEP5)<*01B7 z1{$nE1iY?u(WPk}&z180Nm;cuvWr%$$UaZi)q<&qsMg8g&?BUZcS)49f)Qi)X^>vA z%K$Yb*pq@4QE8S1{SIr5!J?1x8m)C5Y`PLL2X0F%yY2XGsPo!w=gO*pDn<R1hRC`o=m zs;xCvFk~g1E)$gpSe9xu>uawN8&2Y%9#nviIRJ=%Yu%U`v>k-hR9%oHWZzKLJ@{!b zvy~k5H*@;rXj@Iqr6%_Wf619onv-JAXXO0gl2+3&l` zuhtM+T7W04t{D>~H>N*!klB5Ad|<6HHgV^(%ep$}=NLC-lW3hP!6*7_RYF*9l3S^M4kp-DOjqMRNf$HCFQ$+XhL|Izn-eWj zt5gZCKQZ6A(}lb`VyqFG0aX;v5vi^l5?#sS2gAsnW$ke{BTDT~xOu!bKq|u$Q{Cya z3Od$E|6C~<%cpEYXN<(m#lKXbVjkh;R-#rIaSofpE^ zXm7uw=K+@b%wtMZTH_#gF*{x?fZ{fATXf3N7D zLHYhKE?7>!|7@-F-}@&qKi~oY?%u^Ie|4<>+CBNtq}%fF{+q$~zsfzSQy!}ITPi+n zBG!r6aX2|H8cKqyqe3XHjf*RVhf5nC4;BYYqXm9(xvYPjz27~#Z-3RjKFT1~9=Ft$!dfefomz$}*) zEGY9nEo}XXQ3RnQx;t;%wf*!vfMnCC0s$j^u*w{!3kANq5toZikr{=U?>_N{TSDql zw~TZ&Ix&!lhsVVQi;CMrP7kO_{p;5+2!($oR_f7L8Fmh6C^}{$5l@b^y1K$1Eru-x zz80+a>*vqk=H^4ki=s_9$K}AR`J2!p9UUgD_kQ(-s5p!?~%GZ@RDpkn9R(A(Nxf zbHz!Bv_%lHGc5qgz(0ygj^mF?HwST|h*vkFakKdLjgGFnTLvmiIS(M!NIjVJM&h1} z6W1EgLH}xL1gir)b;3{xPE4%EIv?N<=eM32|7XKPOIJjgT1#5imQF&G;w!# z4H6_sqru%>1Hm=826s;&xO=eR7ThIRaCdiihu*_GHB&n^|Eit&Fk7>=Z$1#J3#jhX z=k#-)`@VnIH6Wb*OC(G*h)U}U^(3OcogjJLbecoi6vkM7go&x#l)cmVY<+)!d;9pf zTWk`EhmVh!_v6R;rG!_uVS;i0EzW$j!Wo0CGb zTLv7$;Y1*86KDa_nr`BgFL&RQ)XiB<$-|yzK1O^QoFDF%mF#Ww^8D-b0z^5*ME?Q6 zQ|Gx$>jVJ&cL4uB9<$ylEM(BsP{gM~U0cHn&Cv|S!P@P$Fo|Fmddw0tA!ShWyS@&d zP2b7D?;HyclMol1m>56XUwWJJ6D+;og=2L!8!&-owBEO-bqZ^BY^!1wiF$)bw8C#W|GJOejh%!eYZ!qcBgUQW~K#7w5OA2xw8^-=dV{6*ck; zEX>W!_O_86)55lKlnXx4@i8)j1QcS^fH5}!)=xyTesod|iOL^E%QP~PH$Qnigfb$} z3L1XJVpp`wa_Yz#58rR>HI?v`)Ks+7_Z2ZRQgL)d1TGil&!5$`wSl3z^!UWs3Teg2 z?>`VwFXO$Md8=i`BnbplaMLnVjm&llQVDJK<_##ej!$=p39!gWlM~XC0Ly`avbw6y zSIy!w9=D1(;N1uQ6D{V~)znc{k9qThB&FftlagWWAMKnn3Fnn6B<;>uguo4x&S}Qn9_@gCH74Z2y5*ta(sMoK5Kb7Lyt`tNu>mt7y|~1eK^x@z`Be}$m;=U zj)#Uyt~l|nSqzFxTXYQ8I>l;`rsoXGOIw_pZjDDCN@UFdWrV(BC>8Gq29)te-o;P4 zDFmu+s(d0p%<0UvQv=d-*-dQB&5rgrWd?fbip(-U(8I9-;^bsNbWD_9!O7iVBhHA9 z_`WK(E@^kyS|JMc#x>d6xx^Xp>J zLW}zVv{-<==U08B_tb?IxfeP>z*zSw3awXx3`@hm#x1X{yu5Bl$3%=Yukbf33m@rt zFx-&9=i_3&1GQWuzBE!K)Vjhbb`!}Uhq_=y_oGt;x29Ce@V&l(pN7G`Fm z_BVhlo8C)LFEf*yZ?Uq01Cvt&*G%HUpY{WL?^t?s{DXP!LNza`F`Bj*vRU8oWFKVa zdwPDnsP`R>tu>EWm{YTkM_=8P?CsZ9e7`}G#i{$#oY@)J+bi9j=#tusj}!or#!c_v zMf2wrE_Cq3av&TcW>g)xGegVZV#IPpJ2OsZjX=fVP`@Fgrvm8dz4>bfe&jbvSo5WQqwZ! zTH01{z>xEvkv}sQ3%?5ql_V-Riz}(C|HtV7$^zkUTB0;%^Hvq}xVK;_V@ZJgQ4}D; zdd=O4ow$eyi;q`Ri*}dR4k#`j91O$W%A@WPdudzAyaFzHXqNsy~6vd5%8l^a$ zVOf5sfh~`hQb?-@jg5?=kR|vQ;7on^GgKnys!&dvDc}YlCe`=P$TYc7)()j3EHEvR zl?7bT#Drw6OH&IIi|TI0vY#@tQ@@FBZADBBcEfCOaTbxL@&<-v(ON5TY41IRh|?7A z6o|q)8DGCDgEL2onf+#G za`<-gX(U7LjjAoEIq1r6zOr@^q_ci8vBJ`c=f@CJlhlu>X|B(0YO5{s+wYq1kphg& zO4-az{C)x2+y9(MWB7Pk0-5QV9@h@XUCMq+7GBO`wske{SfKOND4Gm8=;ReyIrvzE zzAG^BnJAVIa?`Wx$La~26&9ap-mT=qZ^chhm~T96ZcbiaBB9GLR<*YNZoKz~oR(Mm zAwX(E4L(*0u zIMZDb6o*fGdRxGu)YQ__OcEL4&mn9PnqQik5Dfv$X_kr=XbfUKa78B>HcuW?P$rZ0 zj=fH_DCO@v5)uhhQa2{Tf`>OJk=#E4Mwt;{C8Y?lwk~Cq`wlbGGhw6+T)47V6B*rA zKaE1o@v+N{C3VNb6&9;Nj>Vr~%{0n}uTg)<^HZ578B{{tzN|Zf=Xz6iKZE6YE0XambK3GwM!lWW_>kcXMk4=PIb z`MIfuQ6gGxUF@JKtuWiZ$Y$C;+L}%VJ~dTH_+{S~xAU&v>*FbWIhKIo79g~7c@K+o zfBs6EP^YKGXd#_l;!mckC z=2eqZvHQ$P-uB*nIO2u2bii6`(}bFS#I_f!?8K6kWH|;Jl}t^7%FAq);}{XKNtit zfb0W?EfV$pu7G!-1jZMR!@hW&->W-KZSSV<@63)xjq1E+G#p*CXB7yfeZXdJ!)8@m z9l_+FMw~+E!4owL!yN=H>hBnEg>LbYo^R$XTIY#;;pn7vaX|(K zFtu{&G&2ckXH#%K8kXQ+b4R}*8>KQ>c1$)Po zWftEUtSY$NT&BK;vehbnH75q~qpGSK1`+iQKQ$adf#%0u+&B!%i|MiOI3fwD+XUEy z63I+<)$J~th=}SU2yHAhXWLCBzjc z1^Iz}*0W-CX41w>Rk6P#SpiR5(dG-xT8(mNpb9H+LiE6z zOE#{5u1IDVDo5ji)$PRnOF&jSGitIW`f$7Nk#S{ngtvwH^no>edP7W6_6JB%W1TZG z$mI#e!aO_vE?)l!n;5sAE!&jraE785Tadl#=6CqRjMVG|3e0w93&~&^%!gPW5S3C4 zLUeyBrje?+@4L~+0ql3(TaM=9DLAp@5g_VIn22-t3_?s=iEXLP;c$KtDt@y6AN}fT zw8`$@HN7!E^d^LOaHiBFzWMxF1-O1qtU|J*#doWTAwqXdBX^Hw2+NybX}qCe;Vutb?m#%)mqR@omH&85E4E&F;M+Ij^-Qn^Q#!j72xmq zLON#u*qgm}y*25cQ`x#_;Y%vuZMg*PUjO30e;%us{ZQA1;zuwRKzrBUN~nYFN466k zdzaE_%DC6RD+m?dTa`WXR!48^SrK4q2sLemep0(dxt zsN&&o(}Z{7Zwfb^Id7OrS6+Iy)NIhp(&{!WFyL<=?uk!YpqK){yB3FU*JB}#&2d1~ zwW8H8Qofpomq>dD)9&JB1@pew5JI$(_M*!i{TRxt=#L;S`8hNKP!SeYkk3M0kO0WU z4EV5MRRfCgAd~p$y=<0B9~BGt0^VOZXLnBs12$~6&)%;W+i)OUiKa+GbPGPl+BUX4 zYs*4oTK!mP^9ruD{boKU*4oEI)RwOl*R-B{FRvWWH@>Y<48~l~!|7V34Cbd_S1j9g z)Kn7k!lg2eh6xT&3%J?LGnRbbk zWf+^|tXnxHCi36`byQu)r99j}Ae+w<-rkv9Xg#qZ+4O|G+|LV)KPkoWcQps+d}3-I z9mj7MRQb-xbbbmZF5WBoPZ}rLbM`BQhTMA06=fA21)^fp=P|8N*~P=HdIox*OdCF< zE;qTpj4hVbfKmsmjUb)4Sxt;{N(&q}_t9!^&!m)oVT174rL^q9<0;#b&ox3HIICb$ zL0y)LE=8;TO4*ok$;aRu_#719$9qh-gIrGACwD8%Uua*9QCMg@C2n-m~!uJHwEP};}GzTMf|4_UrlD!-?D)_YM#d~5>u1}Dlo ze4o(fDfaa|q}3pY)}GwXR@?RIxIw^r@0-0q7^(0_drTa5>#L!cwX=y2>4@GDiXBxy zYS-@^71Sk*QtMC7%~Bz|2Uh@lN(&eP_2>s~8rDbB=PGA4ld^;g*qx(SMD%riG&!wG zH9Z?cypva5J}Yd!OR4^i3s;1bvOe@(tFq3-saZ&n7bG=aR?(*87v_vG8~#2-0nsLpaYbDxXr<(7?XejaDK-U5KnOlL|fky4Jetz9T9jqwlH8 z!NeM+(CrzTvha279$$#84mz&Fh;RF_l^TAX^Pc zrS507F>&Z_*fa$0q7_GmSOmgErxqt>_*nTrvT=g=z%CmP-z9pn{;8mOUwCgE0AbMS z-u*Xrjuhk2aMR>-nYvAYkD*1`NN_cx^?D_H5KF0{NRK=#A<>zCRZ9N#G8Il*se&zc zi`U!EhOX!h#gi?lyH4o=3v3kLiUELunvm@HqgP5u1G>7A#!Cuw*adBGun$ZN!j7nQeT1&W70fO`tZ;mVXueXEC%gg-egAD%H z0;!ok{8yGv#>ZW4kfMc6xwqaD2dPXFqWM*s)f`WLS1`o0s-w34#u?z1|nxNYQwMvNGdwm*vv~Vge#x2%A1SGC^;iL7=uk;e)*evD8tXet)mNZk?l z$ClIU8+0^OAWzBKZJZDWgNxyGxBhy54#1YyGAl-j=PGlD)ZfdEZdb8yZHcI1f8ff* zIUb(-z-@F)Yi?Ou^nGw}Xm%nBNx@rI(oW=kPSfYLH;d`pK6`Er*lF|dh&x6DWhBE0 zF(~-QrzcOZ=8t@fI%$V2(5aJw%aIB03?RE7^_nSZ`?PIaRh+oNdlW?}p z8L)o#I2=WS5(&iwPF8fVP2i^LOUBn}iL_YOa?a<);YJpQ4vBsiHq`+DtgYE{#a^J` z`&Ril(v*_&(zOO*s;TLDBvHl_5R+{qbDNkHGk{WlcX-ml*aoNOcI`mtj%zRRHhDe4 zNhVOuqypxKp+#RA z)4Gu|GYhF9>C71XJ18%w5cwSmoz|2Ul-dJvlrgQ`?*ykdg;p+OsYuYEWSPTvK%Ac({wg3RB)jlReea-8Z5zKY~xPE;cfzW;U|S$3SU zJmvB5@L+)f%m3~dgc%y*Fkj{yVh*T2$|#0My_FyDE}WzitLs@qQbjCOm&8&R7&s1f z#{oI7$B++npjZ#!9mR_494Y>NeH}SGJj_HErvY*B7ogtp!Rp6hZ5sdV77K#*-pK-F zyt)IwknPNq*{_x;x$N@zVheI*O@N&2-u~t(&D{+;3LM-tzXej4b7q>2Mj!sqa0I-PFlp|#H_um`^ zT>(u?u3Snb6Jzr)wmrWt&f}i$1JZNWV`9dusvOATG%1*vINrVEL1?{y7--`sVB?xi zG7paAdS(HEf`{$QLV1anJqt=W-YmO)g}aeNQWjcn=t?a1P0^Lv*>4$M3gGh%17rQ{ zY;W&~`tJ7f+QH5Bt4kF$+iqcB=wtf8_(ywgZffA)I5(b zcd`>;@J;U{Pitdx-M&dhKLBXR3OhTyQ~r0Q5R-d{-^4%i@qV1gX4QmZdvxd!-i0Z& zflOWDo8khFXbko&-Y+(Z>gwNdgElrmF8+j+gygMH1P|}!#mA>hoH{&&)tT}W=$>>9 zc~i5e3*PUu&1S1l6TIbmQ&V*zNUW_1yF){m(FfmCnEr6GjJh=bVq|A-4&XH#MNn=E z>`U3pCOaRW6$?WXiKdBM=mmVB4NYtSe6G{*2r~pa`g=x0BLEJVnwpkI05ZHDPh|aI zq>uG&SpS6u)O8=E={1a)H=QH$#nMXUJCz*@N(4zTV#xb{7Ulj)OH)fr!<@<@fWat7 zVV*Z|Y&NtC5voB1hsww)9`&&YZW)8e^YPKpf)T|-S~{S-H2~MgN1z+FmDO-OQQbBI zH??hHjoC)KK!uZD)5aflC~9^vo~|wcZGMB&6*flFcHBwzI}%FN97HG~A=R#KDT;2qwDU&S>o4xo8jwSjsxpKcboz=UMTNnTPc0FUTwyR)>(=X`8)1ap z6jG?i2-o9J7cnJJ2@>zHf7_fpT_f0DXh%KFw*PJui>)*g!`a&1uQm~uf<^ZNy@n;= z=6t3qP{#^S2BSrmh!vNhwxS~a5JvMtS0@85L*YcxTl{?7L9;)QA9ly@l2gi!#S~=e zxkU%NjzHe$n*8}?5OUjaOamvr7c~)RZR=bvFbogb9l=ndcHC-hU?BwiPVln^&b*)K zJ)_hAV;A$k>v4WnUH60Z@yRm&Pp#qq6cJ`){a?|9aV zJdOYfQSVvV^`Bnge;UVN0~)FS^K%OSr&5uDx9LBjt=U-rSJ2k~N?!k!y#6bB{a5n( zujKV#$?Lz8*MB9i|4Lr}mAw8ddHq-N`mf~mU&-sglGmDlC9nTVUjLQ6{wsO?SMvI= z?4>%WrMez^VGa~Zq*Y>Q!%<;zB%){r?_TQ~Zd04^<%7Lu@XxmY9+Qu6 zKbhVxo@DTXYG=`f!f7RiBmD^=*`SPJdJ)1gF{mlt9-x8}VmKBG1cg{5*@L6~j5?mN z2ZRuZ1RbMzpZuVeI-8u!tVurTpnn;D9NNSOuXB?~VDIx36-$nBb=8<0Fc;Y`%-fDksM{S~g>u0iTqV zsjB(S3gYDY39+9s_3=B@`c)+Z@FZhBhg#V#?Y`KWSg^!G)O{~a zPfu@Y2@4A=dPLQCZ*1wI)WWwA@9G-GlQ8^&B@FQk!V=2irjp2)!RN!B`u!fuJ~07g zE3T27%U4HdXmgh0>7}K9DO!vxK%$u>+r}@jvhq1IQyYe9L2d$7qHBhnw5jhsTE}co@KjiP#GT4Gs1D@?y$P-rKn{@<+zl-Z~0`fNlIawl21n=}ivI?^L|NPPup3X#wdQBjf6qcI{w!oosALt$WH z@1KTR3*M{>I1tEyQq4zRm>H#W$_6^W`+6@7Xye>^@==OdxZwU_65f-UtgS8+CN=r- z9u_7fR7$+R_YWN{Elp8=exZJFLGi-6sFf9f^LJ7;7X57FqAsp29n&$qG_~?)VzqB_ zxmTL}CHf)yqKLs;^D)J~v=fpMV4plav^+8#=KuQ-eT^d||b6R-B_@$H!&E zk=MVc6Y_%_0V{wqjPT*X?zW+|nc~CTL{f6cz=JrfhdhYy&cxcKVCVc*UO~@1{8v#a!s31<3Q zUF|p@3=#QE(8BhSonwA&X=TB;uBpASCg1;DD&*12i-*tfslR}{=WP2BjQO+z>d2Up zyi{PeAyIyW%$@l4+`4(0)@%;7CdA=n;%#hY^|*_&z+6Y{ytR8hy$$G6fB{rD3tc%A zTQeY@=;`!XysC0~Bvwwy6##Ww2yx%3JZWFYcuR2?8#tJLmNmCi@lf!Qv$k*pkMGX| zf`jsfrU%%0TS@|6b|G#)K9B&XuZeq3L+KVnm=H4?FudD8MK~4RZ17Z8a|rLP`Sh_F zZr?lU`E43lF2qcDZFUC4%Fe|r0BUh~8r_^+Y55yi-6Hq>yBlEAv?%O*Gimbaq+2n{ zUDl{>Q3t(fKQ5dLm5R*xiC#IEM=$pO=*Jqo6TQU#V2+=lIRohf7pc>=6e}?d_IzfzkG6fiNfn z$X7;GPP32&d~6^v(e|09-sb*`+Eq5kljY^h&)h#&YjAvIpOnR)|JWq>njF<-bxjPW zHZ939S_5VmbTUED>wUK4h{dQ@e-L`ZsXYb6cjSh`0I;W*9O;k{^?Rb8wUr2R-=Q?} z^Xg-xQ|CICoBnyFG3Mej>w|qVg3OfE7H7Ac)!o^iex>?`Y{8(DU%=T{q}0$rF63EN zR}J@EtF{H`G|QtBUZ)$wDF{Q!OUU_ztX|_aeKW2y3eC`+v#r+XDEPT(O8w*3W`x*% zoli$E7vVbXg;o|8R+hXz-~h<>r4^6*m+gt!-AANLAy<>CqAjG!?DQl8+|97VqJ_%R zyaF6SIH9#iv=5(8_Ha}#y?{wWbNe;eM=1#O?WZS}5EI}_me3k-20yRAwk7^~NR60y zt>DTR5(2%aoi_NESy@nwnIy>d^n>)}+hCa4m}H=Y5KLXR)=*q@ACQ%!x#nvbgM3+E zU#~MfUSFU2o6M4R#_w}^{`z>C*>q5yoAo29`CIpKLgTscuXPcAX`pB+ZTN#~CZExvziDnlhg18i(lW5vw2;g>!VLRN3Bf8ER3SO^g3ebd5akJJ{xw_;%WhRt$4~5PiL30=GjXy@$yWJV6?12$q z{bo$0Ho%b2)!uuvy$I%OJ2lH3%|{n7-ph!NM{0vqYpX@3ZCc zBkw)FdPuY$dQf(n+UFJNBeJgk67cg*(fJJOD~1p{cm}!bndJF(l+ks^gD?W6y>}7n z&by???Y48ocNz0u`|cIW0AhdVK?upu+0_vZxr0RTRseW0xaTap3DX=KU(px9DBdDD zdlz8XSJY##Lk5R2sipP~=yu_8=X>y<^DnPHZufyQf^y&vLcvL}&j$D+%s)=)0Ibym zKD!HL`L$hpZ8G&;S8(yR$`yPca@IKY!LvI|K!~eN^Xqc`UgeSC%Go4oG{lCVIvSWs zVD}@QpulD~D9exbc}>Xo{$>OKu@4B2@)Zgu`;gqxhbhrzhDrHwj*trUU^m#YtSuQA zk@6fppx$|P{}l}K6W$|Dx0d5MCET|vuVfmKH>Z2K&I#f>;^%!za~Ut?IgoErhq~uE z`UsV6cLxg(Tw-SA=h>T$7GHclkJawK)hc4Xvh@pGyXy|OYd>EWkr9mD{=@vahXv%7O>F&uOxJJdjequ@Hjhv@mq<7NGHDyrc^W)y#%vsmIry1Dbv&OtpB?GIMu-27}qGTP7V;^2GNUuD;*YhYl&!S{O z)2-Egyk%x|pD_x*0>ZQFNxJ20*p_Fx58S6>T$?OBP50nIdzX$P?q(&=Z16UBv!D8g zkMAp^B4qo-%Sbi|!#cKRjBxuLHkABNCHu1p?HW4R^nUG5jW)n%ttcu z!)ukCrlRX+=B`x=GAn4z^7~f;+lk}+jQa44TMXSI+20bq8i+NLRa}@~G-PR|ONFNq zXAA#m7~6VY@Xpd+Zu0f7BRHH^x^ysF*fuwKKh45Wmov1%%+gXZ(o8Z`ak15m#@{|i zmZ*t*f~)EK_A#s0I)tZXpOEe;n0QGLlF&WDw+DXNeV`V;yW17xHVXa5zq0Bb9dnjr z-C}Xyf-91ExwtlblD_0jYAkwpv$YR({4peWD|7D&-u{5FGNUG{NKOq*-m48aN$cII zV8A-IMnc_JU$RM}JOejGV3dgzX&YG)O*sM7S-vvP3Vyz-f8{MghZ2y3FJX#{KXo-8}H=vv6P`RRFwaeTTYJNFhFeKoKcZ{MwR z2H~fEiI0QEEQSIoHlb8eT@L2NuyXx?C@-G%$trEJ0^Psz;+B0t77_gVS1U5_K1w)p zKMuW9O@6;&{NZZd>iDEp)!>tOyi)b49z{Ul7P@CskgxAX|9<~5b;)d82^_`^TF_4~ zM~|1+9=cu?1)Q16lz{S2nSH#F%{JreXM)t%ImMZgKVC$q`O zPmgSDf5D0QP~{yI4EC!fWT*IK<&bTnh->eU49H%|md3v7J0z^>XzP4hTlR$c_El1$ zgAs!|>A8r)($67#d<;{J7va&G@mtkF1PYW!f%oNmBTEbUx}3`O^z0N*U}66j^IBi= zx^eN|66F7r4CW;|Jl8<@76u76XV1W(4f#4gFGygnf)In zelfS-4C1G8{bf)AAE@JZ3E3k|FnIQv)KYDEDVY6FP#C(*PZ5aQ^Nk>QT|fwOB%cAX zALiLYp{T0jf_VKbuA)FPBCalrv+}lv2^~!GOz0`O80#n+c_?gYbPI98eoFe&@^_qO z!QU3CXCTq*2!rlZGZ4`f5ZPbSIV_RAWLKMCwy0Aoul&Y=7;izLL8Xg`O)vZ?lODW5 z-pt14V{NyAMg`%Rq%ly7(De_KBRN?*B_!bsoL9@D?qTU)Bh}l~cz>96AL- z{#{C2tU}5D{7;tmlrIYpG$`Pk_AV9LVNiqcB>!;Iv~>pdcQ-qh;jz_bnP%}9fpIgd zU+)=*M%LrdjqoPTb7qA;r9mSLMW|w`u_0c~J1sI*%N+2+7IgX*WO5WsCw(EZQm+Lh zf2LPfoV!q;n>d}ZYt#XOkI!6XUH$Z20{)&^A+7RW3$_@@)I)vZUEYG!4Eh%$Ss_q25cOd1Fw`2Nb_pkJZmJKdMKKcHvacYW|0H2_>DAU8KQR%IR)lHh@j9YvtN zJ?kj*q8FJ$kA3|~LKY4gaz+Su0*y7^u?3kGq z(9lSPJb3VaBFsRsx6H|IC@PH?5L0rFE{@55*MBSX2=I`^utV2Irk0XTQ}VX3;-$j~ zp{A3at}ZVxjbINsoglNecl3t_M~D4UT#db%u#J*r1U@T23<#*Z<1+NhT7g+#VzQ;4 zK+kB3F}bvur{m$jQW{}`DR76j4hRWjHt_<@O!JG1AbHgXC`HK#6|t6Avtn{~$K&B@ zsUGO&1lMa%r5@Ctuzf*t#Bqiek5?j;xtOs)>A>*_1FuedroN6>^AqcwPxG-Kx@6|k(;Zos>TS?;V!}R zFGisM8?r4XMmbkoTH3)uSvjYpqjFg4aA#w8lc~C)uAUXdHaWL5kxdJ0UXVFf=`;Uy z{xH{+V*z+3`=BTsogAI-&adM2 z;>mQ!@3}Lml_0ce0DX0~46ElOMy&?Zxi~v2vblY{Da!_s>n<+~#y&PWaBw@qzj2>0uJKs+yY61=d=PQb)lUZq1}5 zJfHv)yrN9LK5_GVdCMhUp^Zg2`T|V8ZJ+{FX@C1yWC>Ckn%Wo#QUGZ-YBVS|XJ=;+ zNKH-D&eT$PUgZ{Itl;(U1hfz`1gbx%!w}g)Q!d@pUo^*FSj3+&KK0j13h{O{CFR_d z*sANO(JFah|F=7drsO#x0|*=l1w6=Le}EoP6vj%D7Lay44joD?V=?~zLj9J&!+XPz z%~KHU4WW%l0YwjW;=DI{K!Jo1YPq8=9jF)3=<<00W0qFoKjGhCh41*I?tD}Pe zjSQow0wKAIVMh=&N|(juseZ>0?JeqKc^`ItR*hhcFiwzzL4iPo0F96ERRZ#v!fzeV z!)9jKV!wNlcI75K?{gw4*y8vSY~B&a z0`p)Nd`Gueu)No?rF4n}_59%GkLy?TKXggn9snOBJfoYQa7q1-AuFJ_^q&Z$Y^)sr z3n{Drqhn;iEBwzvx&I}XCOgl6OUkiv|8I}Von-E5;Vj_&aa|=4CUCM+jTezopyri@ zrec=H3O&ZaqYtlVr=SqQh@n7;!KO$9Gy?0pDnGpf=o6zoUOd& zeab4SXm(o@e4o!qp$B<@B&{Xf3^j=xQ{nFdd%b~lycTY7OV6cf2p;QLJ)U1`e65AA zK7pBk|H7pDk-*Po^D%L=lMI$PV>D2R@?!(}?(({c1IdEOH!&{^8^co)h*H=WHhE@> zi;3KbV^(}PVxz{RYPJMOq5E7hc=X8!8wX!*TBQMKC-*l-sZ=8?7XN(!Dk>@hUXCtn zM|2#cC`O$ByKKk_LPA1fa9_9h`FbBG7Z-bX1OdBZ=pfCB8xF0QDNw(^z|4#&E+*_I zF`~2_59g>UE=E-B(f%uAiNmtgvPO?)uo7mau*6oz+aEmSHX*v*tDzJ&_@%9NU!SzaC$#VO6)i75Z8De;n+ zqJfG^E=oqkSx&ASI-@O!8gOx@B0p5X3&q4^M;#fm#4lj}Dk$JB{vxkqC;c!JLmW;o zE@p`!iVP7rMrJeW)bzcylo;8qE0B0>W=7(J8(gV?qmHf)1`RtNqMXJ9>Vd2t>M>5D zr2`C=Q}9=IcqzIj5{FZ=tSo)~)m1^Lu!e@@f7xSUZM6F9}Stn$w%jxCGK26DBGJV1R&LRX=ICAv22G?7Mi=(@zr(0A^><2t! z5Y9UP?$(Y-0=q7;oU>XDnQimwa(;Yd3I|%3ZKPjN`r-gd#hJCALnHyXq!SbWxS?~qWF>MsSX72GNlg7YwkVAF4M1{E>~Sj)6Hp$9`^Sf9-4G#{ zJoi71jkgrvz59g~k1+@Z7nS@=R+e=zTciF|l z9GjRsc3I=fxhRO=!+%7{DboPGu?$&_Ql)G~v3z7Kj>&^5E7!x~goN#P8gDH!aqVzb zNM?bRZT@!vDX4-^7>oL^`g{~`v2?g^z);NjKuTk9?{56I56#beS zGd17a-rd2+!NJ4DMJAGv2!Vxt@Mz*)NVS-pc|TZkpE6Ko7y@QvDFmL4cNk)EdhDQk z%(sbNl&{d@_G=Jkmf|k0ho)9K02fu?*T_-kfZWV%2NyRnP!jNxe_9|fp!xG(?hn5d z(`j5xVCD`%7}%pt4hPF#uKy4XywC!kR$f6JDe&@Qs}W_2CfS{wuD;8T0su};jqDC( zH8nL|T`!fKPGy4g?uWyYp>H+LG!hR@yo6th;;@XV5#thm16|3jn#3{RPUaP5NxMOj60 zOMa8i+PHgpaq%Z&<`$wwm9+G<3Qn$RgPG4IrNwY&Dz6K}zZ1j*ukS8gT)R02gT*Bd zaj@8_G-M=^ht%E3g(NKBVNBIJA`p@t%Pb-?{!J%@qyuQ}GC zxWMHy7h%azhG*~P_qxo|(p*x97;&l^jClfPR)!AZWk}&3fNxWS(=`pi1s&OvayK_O zw|HT+1;R$nuB^W>n zde26lA&UG9OslUhIPdmKq&3v3O|LICg2nsjib2lEV;WhNQyd(1UtL`9Zc$gsh}uDc z1h}l0t$aOV4e(E%EiE5)(up`+_ZY`;cg;)UH>t&N&NUUKlgd6KVMQ`&C=8b?N6GgQ z%oKwxkYxkh3#yySYo9d1>*E9aE7cbf5i$WL#>U>qr~3orhT!tk(fZv!Mt>$|9U5Tq2*_U z=$RxF(_*#MHChc)PYXdKMPEKx)_~)MC(WdZf%zY2P)> zBs>USUPlbpQQNHnUBI4tvytFErAwJ^Lt z-5zE_z|Ug-@PV175`pCJU!YAnh9}I+2l8-p&nc>{iwzzgQ>E*RJ8JyPCQ%b8UUXIP zBXNPj;0KvhllsW}cZBJD%}=3{bpAZ(qU+@q6^LY|rHqV!b28w!d-s(M+@wH=pHg~g zj#SXwDN?I_0~~vtSe+Z)jh!MzOLh}k2~{Ggx2A#{8-GK!-`w0JL&1lB*45Sfr1Ngz z-~uz@9d^w-^g*JK_i?z}XoWqXv=rUiROp>wvLnMR<;Lu{=urwQbiF(H)}3BAhn$;H zz$Mt`d2_k*xcJ>HuvG}Ss>g!M7)HkAHNGHEE-s4~Rbx>vFfjEmj4T-tlBMU5exT>` zYufO%I9ng+9S-V89!u7m({HD0Z1~)YO>v*gx__j_GN+lG&BE^s?CaR)mps}wE+1+F zZCq7t8YkD5MhFW{jA2Y(Hn+P7P@@b>O?ka}@sfKS7wv9QS2EKwy)L~EU@_KOeeQgf zHdmIo{k^NNp9hzZr&n^@(`_9xFO7l2a-f~^b7~VmtFr(8V7;?CoA3F2B3}Lg?f{PvXN<)1e&dT#I&zprQC};rQ5V@H&^G)5(zkT32wmUXLFS5X=>mG_aluQby$zm`2(g zR60kdP_ooIA$7l zd|L$|0y?LqL9l-O+a55{PtF}Jqf5Z%`W&RfBd$MW8_D5e!@h|(xwhv;Fy-`((b{ne zk`x@GA}x+~xkhkg%{a0D?gTBbDjY@C`1zrU;dkV_{PB<9vM#3RHZXZ9i;eUwoZ-(( zTXuOpyBhJ?MWqjSe5Iv4nZkT=+cIj2=b)dRee-o0tp@KRFR0y|Lj{rp(_ zeGxm=F2T-{{NuR>&@Swg)TJZ+yovZn>Gs#FWSOl({dTbzIP27>A8)1ml^O%QzY|L@ zzZ+Z0CTUyo%4>S1y5T+EgCKW%%}unE6!T)_+^4jy&`z}o_gh-J$pUI z)`CkGrqa-b;i$54f=e$ok{0-U@><_w_j1P`M%l} zVYDsXvPnxqx-Q3aLj_ylv{GGk*NuNXgw5-Wxn6y?+owOh8~g*-gYfC0Iy~bO2WO)B zsL|e@m0z#7@cP`p0v~rKf82JoheVgxsIl|NytOLekDv`7NG2+;I+GBvE<8z}t_@dk zS-<(cklbFh4$O%_<&l+H6rdl)9rQdpJyt1Flz$n-o4`KtjD8w$L7OlR-_&csj(n_o zEfFls`))e(A)j&bCgxMkH*Y;_R`xl}hsW(9?Czhe(>0F zX{4BEJvBR-!zr^s|Nf?X9HDKjy-!)KCJ}9&fxQjO_EwtOlP_5YiFk^g= z=w|l7(B}iqP5I?W*zpt`ZnI+@w^Xd2@Z^3O!(>27^W}Z}i}pn(1RaeAjS?c~pSsEl zyi0tQvuJr)RShhf-rzLp#U84qy)>CY`nnXV<;o17)*gV;CgT~GkKU2+@0xs=gzHLHDAq~GgW75 zW-9+RZ}+=<@7>)mz1Moy^SfBM%Qj0+Zp_MpkV=o*fN2u8psi0$t zB!UEDq7#}q6HFqqF^C{30*n+2s33%3U?7C3LLfy!ESK(c?dkRM(ekfWaOlI$RjZ!L zO@ehtE7{wPIJa(>3ctq?OgVq@KGdxb7ZC1XJOSM(54T?U3&uWrF152N2k&N4?;>8> zO1BFYCr*o?uSXi1GVMI`IeA>=yGBfUgyN%%d=PRB!TDk+z;sx*CN3PUAV zemEYetc%FNAm3ybvhbllEnK=kNP)}YtA2sXGh3}h?n=~(pcSyPu(0|KQ(T##NLNx_ zT`Z~x`XC|k!B5#$(AHk*J6baa$C-7Mg zs4W6AQ%_J(2lAm5B+XH(`Aaj{hrf+%c(<6{iQEHTZa^A23+h2G|)ux>4T5&%&&kg|?_{Il!#t!GAz>?eAk?q*gR=S(aneH8HS@ zLjX{}K4{)8WF8}O6T}cYs4#NRc+g%~jRaG?$R! zpDw_-PgMo)h4cKN!?u2m;jWAIK{ca?pBgg(;bC!AS<95V(4ZjRo=*ISY8qOYPyM+| z3-gc2&;9d9Ln6`v+nQW3+FLDkO)d2)Y>r}pVeYOP!JEGKNo&=Rj!WY9KY05NVtf39Yq$`u64`%Ff!}hKi1kQWZi8sj0CO9G=@pO)MdQ5G~$} zj6`&7TueMH6n(R6zq+E5x~p09f9HyZ_0#cEa5_O|F2tjd+shHW_r=$Z9kvA*?JEC4 ztGK!Xuod~VSj~Z7?LZ7dBOTi!GC3;ZvVI;}|Ni#Q-o>>Jb?#QVN5XYPPeU(Y*V+lC zEvhrkPQYH>_=~q<12G9RH#>cAUR_yPTTzknDHSCp9TioVHh^bI%1%fH*bw3;Z+xF@ z9m%-LV+WPFoD=tUDHFBd@z}`wba=a7gMo*=6ut7Qst(U)&DRc1 z6%~)-g07~bLq}g-*U-T4gMdRK?}v>EJpBDCy2n5ns(Nt^BSD~3BPAHg906gX!(=5z zW_x>>smUtJy45x1)t#z(dOzpp>~*pSu5WKwjxDYHDr-6vbv2dyzM~ov3(QeCVzQ^upVk(@WGQqLA(BkwqMN&+ctp6Lr!t z@H2)#2Nsd*;o1&>G#UXG++9r8R{{H%t*uq>s!L^OXK5?aQmyJdtPgM}wsvxH z``TG_Al^lOoAl5Sh=ciX;hM(%U6aV!=c}xjv);TUhRUw$Zls)mxiU{5T;yb>cUC508kLAG$=XX?Stgm))~@rUfK-WmbMyP)oyo;AJBoo1L82FfD|~_j2~r z@g45>dy0gN;kJ70NQxqf8$$1XA_{*mN%Z)BY!cjO1; z#3s`bnl&8aX7o8?co=(2?H3m~`nNhe->zeKv2y_dh+A_{Q{V|eWd(G?uYe`5x3#f( zUTvq60R5PxbmRxPo8mbQuYL;%ojYWmIlP9gj}4-`eBm#De6%%GX#4DL5#eK^WxmMH zPVxFO@%PqUD4%Mof6dMHV`As#y|sUN>+bgreOpV}d8t~22$n6;%FLv2;dHe=zZWGg zopGG`zK#N#K&5hSJbfY=`>p16R`aZfJPeQ4lpq=)6&#flz?@;9A?gxjGPSN z9k)QQLv=?N1N*BX{1DWSmz-J}dX(qgpoILlv&`z4=9FSc4N3=d`ka~xUMe!K zv3b;a4G0bIpgDD>>p!~SH7+lx&{4qAna`u6>FQ;8vKg?kR`_j+UVU-A^y5d=k{NZ> zHM~#VxvRL7kBf(bt)`)~ImjJ_v!EN%{Qd94HiYbgobVZ;h6iYIG5{Guc@=xcEqqV@ zX?mJI(Cc*R35cWZr$Si*Swo}O+aHrlDa9e9$xj=bHEhAUj+DO)t=$QMw)!WdYDhUX zqZFMEt^;X2hS5CKer}CDEYkF;^&=dfp0?tYpPsU7Y2p61qR*k__u+Z9w|39V4DsdU zCgURx?||Fv{CfDzns$zh#yhVYYx)UM19n1vH{RQE|3g5noppexnb|@2XG^n#jkQ+0 zmSw$e@18D(dvkB+x;UX;#pFtfXjnlvgMtMNUXb>le9GSub3A;kopoKIqI_I@uMd;~ zc0C?&hZ`G6WpRJz#=!0Vb*A&QI!28?mv&}WHg=9dZ|8|fl}IPKoGy2br#XRE~P zFs!%WVRv>l!jcJO`Z(1*kWiwe60E6?S{A}pF88oNzcZWNNVpV_!&K7++`GXq;&R^} zs_LJ9EsP?ayZQIrc}EBloQ2V`qKB+5V*2pT&C}XrC}^!eQy8WfQsQK4Ox6j~2ajk^ zjnP-ynx7uycD&n%66^gWwzv0tyxOaTf}Qk!gulB#Z(-LXasJ8&rjubw9p#9|A@=$m zgOwHH@yKV~;p5TaHbis6HY7U(4+d^BOYahr#y*Css-9iXs^HGno08G(?c~9%M0QBe zgR!UKjqR*VxNcxzF7i_w8D)=*)G*#D+tTT+v)7?peDgIH9Z@YN3+D2Q8NM@ zE%&WaFp)QCRD4=|t((p4wO;n+WF;*KPF81TBUd!Z#KxepB_;ENVU=6jY#-spY3xTt z3p!$cVs_pVL$h51i>N?6d>>;0(NAIQm;o<%_yy2Jqk=2vwddb}C~p4WBLe(Rm$2D%sDesf&WN716saDVv{ybnMWinQoL;5LeRK+dEO-CXW0Wd8k(NO7|{O zBct|yj(Hd@T}(J?A}btr5Q6j@j+G*D@5)e^@ zZQ)u)g^=f2T0_=|zX=+S0f$LY5NGTI?TBzm!2dbygQ&>DI)-hO1at(^pTqLQ%`?^p zY>5bF6mo$S&RyJv4R4m!4EFu`5mvzUNc0 z&c6E9i@rs6y^7h03aU07!WSnUKGX*}i>k@J$Crs9Kc4V#zxolYq3VFVr@vg@aLD?h zd${M#dbCH&z6T|)al!6JuJ2SlrOJBRrDyBu`ZTw{GPi5iQ}?Uv+O;n0@o5?Fw))vs zQPPini6wC~OK)BU9iL5CM`;Ht)dGoI%Nlyx8{ID62C)NPHqOMkULm!k9u~t4GY|nojg4yy*{9(=s?8Q+Ta)A z{RKi~xZE^dMU~YtP^g8S>)Y_)ZS#ttJNF*+LLMvp4jJ~IHk<3}CeYK-z`)MQwz#si zEU)89NkaLVMSzfN(D`yG+DoV3+!*MV|1joQ+#=_k}<;b_$C_oYBxjLXoOTF142sA^(iQ10yjs4@flh#*+|JgdGv=F8a`P`{L!r6I4o z@ZGi2@2d{&Kko*s!_|jyUrm3*Y3zyP%KI z`>^{uJobX($JO!r?YEkik&H;8ZKeYvx4L=;gB5v7$31iaP7)PMY#HSubJ}_?kfs6q zdb`gy$M5~}aG50Uhu54uYgD{(e-82nn+>@Eyiv9Sl(-J~Qyz@dmr>T6#c|$Rkv5Gv zbj`4uxt3VEiG4+h0=HX)*jPD0xDKEVdmi^sXP)mnI|<|;)LA1{AL=@veGfM7mPe@Z zgwY;($H1ng>5It?f*M@uwDa!UpDM@Z=V-0V+95(m4Wd$2gwxIfyo{2V?I9Zg%@TLq+rgFS`Cl=FT5^>+A2N6lwz5D4H=W51#e%{2uqP=-#VnGa6~B zUUn3${r@bh`b_0n}ck7+`lcZ8< zeZTgWyJJ>Mo$3N~-EK`Sulgrz69?beKImuL#<8fmX-OycE<46)4ep<5FT2=JDNzw; zbT0yFS|a)Hn@ey@t?P~_J%{q;l0axpG~QW>1oFb z2-;rk5fk=>l-8dQFeb8CF}vCYfM^fB4nS2TOJQBfe--YgDYKHoz+dTA%&YE z@>GI&)Kw#%$jXRn$%~pwzcc@{0494-gubcC;SRbN8wGebpJad{?HP*#pt7};Kt?v2 z&yMAVEX93~o)AK%TGPlo^&j2Y%HvW9jjHE zhxBaboUQ3(OH5c441&oRYL%0{#e^=an3fGpx5On@gh}KbD9fX(3LVjkm{pXtpV}NGkt3dhP>JQ(=js+n3Hnp^rYgAX$(oRpy z#H{?TT~bxkp``A~O3O}2#DcTellzfVRZr-6O!GWZa7-t3xC{3LH#br?`+6z1k_HL}}}Pfu1Z&CR`_on1|3AnTF4!C%8aLY?`$(RMWe$!kW9 zqH&8j%cH}iBzgfivYWZ^uMbh>M;e?SkRijxCVOgB)Ri^5)vCQUswyhF0=O;ZFSisRc8bG*2MafBskdPDHWpDgwYX_!|bbK@>nTIq$%-@CoNU%>+f2rxeVBZ#k#h>VPk4hNL<2#6gY zx5U?N9m%1&0cjp+)@@2qK&22rS>6I0D-fbNJ+D`cNEdQKHJ&e!C=jeYb6cGjH;+DC$1IcO765rm;m>>$IWHT zPUy-EXxaNF@%~ti>^TB5cyGwt$tCo$KWT@;pzf)e<&lYRc&pUIr9 zl}w|1`fP0jc5STxNpg6DQjs@HsSkoqF{Nk6#5AMtu#wrq;zbr#;vs4u(!=9FD@S?4 ze@BQx?!rE#t*)|>Z)tHA+BS+)92jhAX;GiVnM=jk54I8U_F^wi`?ezS!RqMfh}a6>=BA&ybalkogX99dWssm{4knJ# zBsN#z!e9Kxtf{Gq(uYbXCbYlekf@S>$oTsH6T zQE_KXP?7Cqt_>a%_8AkVRz#B+*^k%I&}jZ(@*uDRpcEjGc_9C%f22`fAVX(HkBmZR z;Fme1XJ*2qZ;l=bz_M`OKcrcby&Z286pq6>mxLl6w5_!IDtR#+{Ia2dTy)d=#CPTN z%a($b;kzQ}^%n9$?Q3lM3q@!DqdROH!OBb5^|hYSu%CC4h`DQ-SO@u4ZZ-^!k4_zh zOw@S@ByZzNuiyL^$PR@d9`ZseUJ5#$tJ|E}?-CX)wK}tWk7G#lC-Mj`az~)kk704_pNSI=jN_OP(Sl|@HnSJa=Su#1fdp4LBmQD z`T{$MW~pJnWi~a$n3l_uTeeMpiy|LW-w#xOczeqqq)Xpy3n}?MUy5@=+U^04fQaL` z)|i`lCG)H}9-{u77{5!%0aqYQB-W%t!J-ntw|GIR$J}RRyr_LhI!r|AO{-w|gUa_u z7#ha7VX_@R)pcQ>1H~*M!^F$LX?&n>fhlf>`GkDQmkA)Yx{quoD0BV3m$O7xQ`Qw4p~>KDt0-N^{G{} zJD`Kgz>grAk@|U*BfO8*!@L>qQ{#`^k}}}jlXHnmIBaHqmlw(*@``?@#6)a(0xgUu zBZk1Z);zDvk>4B^P-HhEfW)GMwOk0!Y7TS8yrx~(<N+yF`xQ^(?5Zrbc*ujjl zQ=Q3QAAx*1o*5TORyM8H>I)AztjkQ%^_R-WR zNqID=J3w%4CDnHXoErf-NT!n&Y>JA3Gf8BE2^XC2PyzIwY%pK{V!!lh!Ka-!ZIL-5 zbw7)t<+Mctytd-zE)Fi;>rivo{bz};hhV6Ge``kqQ54ME^1gOKkE;>}gFDk@I5KL) zWbAznx_)gZH?1L>{*X-%ID8BbQxckfl|#RhGSSBj-ZCOYWCm{wMUiNWCy$TOFj$b7 znX9lpa{*v{;H22;c*zm{9t_Tzh6}RL8Tv3)ebMlmHBzw-Y=(8{NLb;R-~*}&M7Y=q z-e)lNH$LDwVMx~*-Aks)Og|$sC$SbLXJyP@X%*+Y6Uu! zVJ%UZ!Qd-3tUH>(gssfmu?@L@&-SWE=jjA!jBVLmcJtBpNp#)eSP(GxJc2+{F_=Fm z^>2M3QjAVTsQD@d%P;^#JOYmA;ev@7&`&9LmV#gK`6js)d%kyNGu zH1e!TKyQrf^j&+IfG$;jT-IUz%F1&6R;0`~H2 z0`iq4jnHkzCEDv_%Z)W&a`>x#sF2*nO`OeQ87Vu>A_EX=az3bIX9g-PZyZohPY=x> zfniZoAw4)ihE}l_cHl5eEDa zky`M8I$RR2kh+1yL%>nuOV51_mo$qKEs22ej0L`<6cEtx?9bO_G6pO5V$zs8WmH{$ zTRUBBic`M|)10W4bt_TGoLg}6Ch_=uYK0Ij!LJ3YZ!ok%O3``$`*I{9M9xS?NS3y> z%{5?Kj3I?(;^+Cwa$mgH&-XLu-$#{XBwpLcY6bUr8mCIh8$XkRR#m966tI@|j^|Y* zm$mBR7?nKhkf<2sT?wZAS`pkpqo)w%U3+Ej&k)3`NlODDz)kMYUftF}()z<$KTMpp zQvPh;{xeiMSZ+Z~yAW&JO1GMYH3a%Tfz{Vj@Au!3dSAVP-d27KdaVNkzTE%fwq}nG zpS)p*>aINa0qP(U?a#@UQdVNU5P*+smpnMERsw-Zum`C^a7Tz9{|qs4W_8tlcEE~7 z(b-#5>!y?bXE)va=Jq7|pO1nTv}zncqpNBPPBw|Rt$fKX$$(Ewg5q?Sn6TIk1}T2EDmL+XKFsQ;OW36TPzJDF)9vsEe096`8(Rr?P9@5(l1vPO- ze{z#5-H8F5P>Y3N2k7h&2Ij<>@WbVm;PRjDwhtNM{F!=!1uyp4jz1&CZT>WI1BSJd zym3izg*e=AT?dKPQzXG)p43Tp|I(;z|Ck7Q-YfT9igv-zI$L!990UeW8|`S~Tq~E> zM-kmLztk#g$KFxdPjMK@%jC~Us5+^Ca$|wahz1|Y^^+@2p0(G34@bUB?NdXbg6b4p zZp#5yR%Dj;bx*^@FC8s(hMtKAujgsL(%%OMyW#J7DJ$i^&9MkFuJ@UV{I| znjX72Va@z2Wyw3+O4<(=6Pe*bw-+Ru$$02n@Mn6w4eBlF4+lcb(Im*f24y72;zdNK z4pp&-KgG}UB5(@0l{gB5lJtE~_f~C>oFPl}#=(+=x{ojdfqY=p@0mP;VW>ziGRQ7Z zQnWR;AN%SL$^@mKk7vm9=}e!dMwP$issaa|uI)>I{1;V>YLk18bL9YHUG#y=|v#I!ce94&hpEO{`rj&x@@|gs(HIYSrlhk zwubV3=3v4YRiBuo7+_f-*!mMZO)qx0oVeyr#1O7RM_>^!gT(@gauU^l4utRzss!W{ z3CAzQVu9d+QxKk^j2=b#6ScZek;+htF{Z&-2dEopJ)y{;q3HG(VBed{SuU!(_id#> z1m%w#C=J!o)<_O*Wy5oQMetz#G|?I_$}iFg?cfZ*!*+}BrEp(-D1Oi%&xEtqR5->u zF*)jy20ONKQd4+OQLT#<%9}l343DK;EHFJRG-R%sY#+<0lgeOBX74PUt#OZ+|B}Tg z)X2^{MiO%Uz#>(^trQIw{qA0_guQGrZ2u7v1&T!mwB0JFCUm20K*+=DI!1;+D!qtN ze@ZwsZ`=DL)_Q~0(G~G}_l~BL_Yk@ihaJhdfZh6><)hu@Pr`ZaiS30ott~P3m2V9FzHexCJ|3%}pR~D8P6zZZB6s20DTA4=%ey5giWB=2`wQ6P zkHhdv(MB!SDM=Q-j?UFMZ}**Xr$IUfcMC&Cl3ch6SPIPA493 z*H8#*Rg;Lsb6D~mwhsCho63vpppS>xN#@IEmdp!K7EiZVyI;~YvDW%ulHy27=IeUe zGshR1Z00@9nIu@GIX{(&Ig6epc53BL<*y8)eqa{r#S2YF043Iw`jUwD&gnY=%cF(x z+b?pMlg_bF`tss&h#vj?7e!ysWM)=7M~1;+)c_Q%SD?$a(LI2rx?f<^~WkY`;z$RMc_}AtCzn8NA(S!eI zZIb^N* z!J*)>81(<|ZUnsdfrxk%l>eJjnCag|(Eqn#UM&CZGU$I`UK*$)Evma+h=PU; z;_*yC0urL@j8p9e1f7#1K?Tu3KLqZQMix*As!w&~-0OMec5Hwj(gj8AckwC_aES9e z-=6cSxcuHc>v1}apK&J1!v)=dITRCM2kStdi}AOGXfl96v|FWzHr8Ude>jL^@4Pe9 ze&+;vK7sm93&6ZOu>4#AnAgb|D7KTVTmR3G8*4~TwwH_?u_qL(_23NqUpt}uDg^!D zl@KJnoh^vDXVN!QkIp-7+Q0qECg66xq1c$kIB+w%3g#gUD=jUp^$TPM?~ie-s;w2T z1K9|8^hwr635u0VshN?51LYxCbk9bA2G}1+yI3pD~+$CVYiYKt>@P6?YaJ z8Trhh^ff(C?9+H-eLb@6p!_tz=qmr8EH98;WPs&mYiny@U;q%k$jbVO)(6_L>rp5; zBxPa~VNcl=9EmGFAm@WX5M0m*nA+GFktk$VgOuEn)z{azv@|q8lw)%Qd|-7`^abCQ z6%i>Xq6GYTW!?Q7n;XM8IXE0YZ@B%!=j4djlM za$*rugzEgfpvCdwr44*@@@^k*?|)3JUm%$|u5UxM&XaU?bI#6c=w$nvQq+@DN%vX> z6^k6H6ti$LziO!m%q4*EcF8_HfY1nhhJ#O@41!>7=V6fl40myVe|^b$v?usAHm?bA z>|!*%oS=SWrECXBcCfUxHK8REVh8*_F2=$31WN;jg@l9$2L>RU>+0s_;^ViJ1g`|J zIl8-f*25JIZA@$}zZ8q(*LblXk&=9lF)CvV?aj~7lsv&J5;^>ZNtgs35*!@zhvg+Y zIy3}p&B!>f@Q37OKtv?ysNtWBq#%WVLh8@5O{n1BW16~aT;vEY!aP*oUD!ws^J;RX zv=tQ{8^McDP%9x3O!mQimtGK=z>kaVGZ#u$=`NF>dk?kut;j!_x_|}808e~B}K$UPf%k)gTw_!zfcnsl~OE@(lsEEe;B&g?}=SBShnFPPDR8LUh&o$AYldTp9`%VlLT)T zV-w^5Sk%x^(a|9dUK43s>n=|bV)&0 znDAi&;Ulll%O26Tf$K0KTfs3cP3gZ6rj6qQfKPzDQ0^O;c0Q(bkq#)Rgo3 z<=52I063`v0Vp+l_ib+*85b2%4+{rDU0YL4DMRls5Q2$`Y^nR7wVMNU50}uV4XlpbD#*l_hrVjw(f+cF$%7flklcNGX>`MPZ+f$Bg61 zhQxucEO?U!Vm6fc%`>`K*JTW}YR^$h5>`eg?1UK7!jz%@)s4xxkEn?Y4KwIyL3J%P z+lj}PMoQPssi~^X&8L;+-Aye`+E%ufT@@XR@@jAsJAqCb7ULfvjt=(DPW&wrKSV?? zQji*plqE)+qLtU^_B6!95SEx7pr0x50JdfMWsgC z2bc(TEo@KQmV8S6?9Gjxt-TEaCwcwMtSl@7TzqVtlimfN%EIPHSi(uzS(74)g7o+? z2{B#@mH>!Q zHD&E(mbSl7j`6W@umR_^?95!h2YW$#uLpZyUt3pCM{mHIyS1{hTEnHjq=18;8C?kK zDZlB5hS5`={?gb3H4X?kMuqIxx1a%|q;HNN%Uvq?wd|ZXu<<@+f6dAEtGhV7+}YVd z{jL-c?cKlkOB@bq->ej{B!oV9-Y|bvQVBDk20XtJkc3FKh9IebfFOuMn*b!Q3M$Ns zhL31V(Orp=KSD=RV{111x9i8#pOG;Bki431PB^VK7)b19Q&V&JJi0En-llfHU+CMZ ziAx&^+lWf)LP=S{sNh3TQ&FQRf&(NkK71QW<-L5y4-+R~iS_NTsA^H(s-K7CmmE?IBaJ^q$eOjrrlbIEZOA(2 zXK0BqwYa+8BgDvpIUoo3Y3cp^xW2ryvaBlD4l_1BpWkyfql(f%kp{P%Wa}-|Fwg<_ zOLe?|YI`?!oDn5ILvO-ZgemsR)W@Bx37iKuc6L2jYy0;4`kt1CFY(N%BodOJjV<^B zSy7ZorN?+95ogVfEyqm}iwcPW*Yc{+@$dXBd_K7!)%2`E&X+1z>ezut5I}nEO-{#P zRzi6Ad3u-Se$Y@-jZdkNCod{4PZ$}HidI%taKzK$dEGT&jF`w8h`dhAtoeD2kC&e1 zbK!pUc>FXnsRnVM+UN#`bq*`5*X!9{-}B^sbQ!cdp3uLkwUSAfTT3ye@OdP7{FO%s zPr*4FUS8cTn+L0~2Xule1&AaJ$VOFF(RN;87CjQTKQ= zHt_YdE<1OHu8=kPkQ-lh_!YZwVKoXsd2v%p&6HoXF&}vpFWstXaeG0X$;3xbS_Q)l z03>lE?j@NY9iOTy1A87Ui$NsKh<><%isOn! z3bEK|O(ytwaeZLtdI>XA9y!>LAUps?%C<+{r>Bj9Jw(5Ib@{uzQfPq_7ZVm7O*!u$ zlvgJ^Xj221tP-@E>O5$X@vE+`SNV}ACft&^oaD%gz{?n_FUpjA7y;;GMzHr&IHc9e zZHaXAUawhk9i2ZcwTsiAelUoceZjA)QXIFih5q$0#Uq5i8ZH4@pl_ec`Sg4q7p`hd z8tCF{>RpD^2#t>4Q`f7&5fEIv9rIXyR%x47t=CyY@OLF*;nFa@$LR z+pGU_-t$9NBKC4g?>Dt{l*HKf+Zz6gGA0#lW6EOcvpdI{G>wG#bac~kUEj^&5!?tw z2G_ANEqh1#E|0c%i&XSEC3Y+CIW=MDLkm5}+r=9KmRc??Us1t=Pob!ac#=36mJjcI zJxbwbPIMLI#m!#HXu(7WHElL83pdkN+ib-d4n_#fq%NQ=9*ZjI2RX%`L84<5 z?euT126q@8ph-$%{TN`w#-tyyNn}2)MXL)vdH5h1Y1&G<@M7T4UFu$ti&1;}a+U;dLHO5eec^ru2z(=c zg!Xd8|CQzy_slMQ;%c&?JKA$nd&ai@eEaTBNO+TzvZZoBhy@v1binnTU)YxG7XIFj z&TE8zdo4cCFJ9vIY3Pm5AERp}AL0DKyx&XJ{T#VgGwRug({M5H{!{bTtBUs~(bIkW zDme|YZ|+MxhBqdN*rngke%&*)M>j70#lZ9GrtMBn8-Uw|+hbI0SFM_+q52XuZ}?3YB1RQ4zZ+z9)p7XC3!bWrhz}wPWaanS#sdAN zK&Np4yye}4_YZh07u8(?cgAZsWC*m;xvZFD_S9HN(S}T-KBreo=qhYVaT|G$vm=Zz z2<^yYj7C#ZQdte7){tk#6E`uo1>@jxH(ZsEyglhg)MJJRX?zD=F)?-Lxf{fLbWl=K zV_Zo{#^LoW2nQf{ z`a*-^O9?ZwF(~(}Lnz2m8_YJt z{GPV)2fgJ1>8f#ez-$NhX7r5M)5X5g>+*kGWe#xy=&eO?{L050f9hF97wE?B|r3mb;O({z+`?RHj&}(0w<`WfRC*B9GQZog6_IFcc|Jsf$yxGm zUc4`fKO6iRKS;1Zf0iI{->v+p&*L6gk_fsp7#4R2Qv^BmGZJ#9Kp`UV_GqDWHkbdG zS|5r6$Z#Oz7B&mqXL)cQFl>;KVDZj*VZtAyacEZhbDsnu@?a&pa)t}-1se7HaBy&z znE@^ljL2cK|Ha(qG^QBI_lt|MF^bu-v5Pv8k#O<~wADQEKu}9->nt;C_;`pSe`3Aa zHXbBaSFGE-u8fatmH1e>?(wn}Of`TG3SK2%dPj z(92UNC)UIXeYMOBN=d)a=Ehf&xKvWke@1-{fSLzM{hBFVqFBDWt_|joHn-L7_cPOt zOJ%@zkc1egPqF+FGUrhl)}J_Ut6yi)Tc<)P_!)EFX1>G>gsu02EL&Gw`%7srhyxuc z=;$a|*ICdk475sx^+IEWF|2<2lp8mb-`=Bs56w-Tx z)AeJ8vpKeZHAbWr&+q^h2s7xWKGN|`2rhQ!*Nk*&+M<9|`l{+QDO_CK;%`ryM0OXZ zC7(ycJdtNVgkfRf=VnNLwDB-$Zqubo>ji=vzO-r5zlWDnOeWI4rsEKZHo5(#U6wmc zDVQkALtmD{)njj^T3cI7sh+OIEp86nA{*8T&6kDJ8{H(Xco7$}4X?<`&MdVSpte(O zadY)>ZK2LI31a7G$1Q8TUf)qJALzH&W6~(_*#P_g=G&~f@e_J$S#8IJSx?c4IUFi- zMf3vFH!vPIGfS2f6Qi+RMR0AS_7mQ+W%FRr@mtMS^?pMX-1yK9Y1c56o_POOaA9s4 zRhpwmo{H;T1$1+v=WClTe+9nsy(?8beXf>N6j$dph8)M!hpsDWQ%=3$&u(`xu8tE< zLVex%hmBwLLb*`IvNN;_qR7nya=*+frcj66UM;iDfdD4c<}5R_ATV90)mL0o&@MBw zom;<$w5ses!)%cs!Na%Kw|A>S{?I6N7+)~Y0%}{#1KvcJ63HT666p)D_w$eeFlw7WR{4vp(|*?UUTgcIb`8E} zS39bA#a6AHq*`*qy2*2Y{&Am4z{CDC!cCj|Vkk4?@H=NU9lvl|nd44sT^5X6g?X!+ zn_Fw7n`WtdT@D91RqptrLbHWSd2FB*L$2-w`ZDGE{P-%QacB3l2S8)LKEt@u$;!r4 z=(-IY$}zP};w0gO3-$`j0f}Yeu*N6T3)UKN{CK80_vzCoi1Vig!ON|z;jS1+Evy1E zOTDxyJs1ZOK@T`vk1yN40xfzxeb0iIu~thuE*QZb#!QsIk((ts^7Rxw20D03?M1x2 zy-7Dur*HQ>)H5^6s_j}3LOw4{&oCCENy9iCU%?J+@V|I^dwP1l+}a~u)|ANvdzOz) z1#%U+5Lp)CaxiX`ML1;Gn*BV!(wz$z=a}Dri#6lHgjJv@{VK~NsuXE#_SIHg23m*j z#nbcY_4$5bp%&U)DP~k6jmPH7bhqLjT8@*m_r=u-P6q|SeeUy?e%B}OVUI|-tK#)z zPwdAl82c4=hK<4iui(o?wTjM^t0zlaN$mwFE)Hq0fuk+?QH5i91gu?u;rL#dFadFN zp*(4o3oB3V75J^91}J-*Vki3|8U>PVleUZ>HLEI%u075h{i_fNYmkzn1z85<0b4vu z3z3aF#z)r4g{4Y}BLZ`t@|C%)Uk3*a-l`o`YeOuB$WM6T!x-df{m~ORBtm;S#3sBM zBu_vkuQeNBomhU^675~0Urw=g#|Ie+)@hg&6Aq`;MD!K9^T2>3aF+{!`ps3LFhabRQ)B15zVZ6=cvd{}j1z{XQhUP17b4#wl5^Ao4;mh&^o03hn~s9TPWBx4P(Jf<5;OiXC7!t- znYaZCZCv=GfpmwLqy}Q&_HYA&+;?ac4T{?r=8R}q`c@?i13p(_438$>1>cdDRg<13 z1UK62i-%e?C@gn<08`-S?Iqk~ou`|TBMV4qv&Bip}E>x4y`9tZ!VJi@gnsdm`4dsR?mHw4#gHl4-J;KT9me=@I z1PV&fcj(ih&=WUujVdHHv7P=L8CyEkAEXAiwI*Rp#kFI0}FkIiz z#0P$$rU>xlf3v0t6I?IWGTp4o)8I3$^>zSlVm@}P1rxJ+3L0me+J=Y?jVhTC;3<^J z<{`ARGz?%{&1mE_v$GxOlle5JSi~ESXqLTzWv3`C_<$925C;}~WzooLwFaK8D2g$o ziKLB_q!b!>#b`hi<}zRMw(ro-x*TNX0*s)lH>~`f(Ma4Lb-292Sz*gegOiZXbRDH?tV_Tszp3h{*I zFlez)2K7^)w4@{Hm1jMO2PY}i*S4xPC02D3C8>X<)*kL_HZ{Z=2uc4wt^offnm0bi zp_gXiH-2APeoXI>Bi zO5%bRDvFckU*TD_oCUsyV;fp=5w^QW4*ifpdw9*(CLYVe2cKSk#V=|0OquiWTd9(6 zCt^hFP`Sq;{-)zx`y=ildAW|5y?T$ve_@;dc0Zv8Wk?ANa$CVUcsz(K6V-?*I+o4D z-k#~hR{_RD>l#B(I(Ag6q~bw)+pB4<;1;DqyxMl}U(Irvpjq%(8poa)5zPsR5I?A0 zQs*WfZc@m7{r)%h-ZCi8rdt=r-JReNAOs6;gF}Gekl+>w?h@SHJ-9<4xCeK4cOBdr zeDE`Q_CEXUed_tK-&3`}@6S^;H9x57=I*|`SFc{zwbqqldc@Rhu0>*ZSh1HXKeG%1 zUki`s>H8mBXpu}Ns?{D4*fp}B*btnuh-I1%XO3nU)~~G(r}%d&s605}x}d?);WtUe zDhpJ~oYblH7rg*x>Z=Mxy)Jlgk@u1UaT_Hgf5oyZV_x?kE-}j-gpcf+S-0r*vI$rD z?PKF}SFo-@AJ!$|evE!{bt8{dTbAPckPbW1!JKGreGnXx{5JPdY^B>Gc|n0U$np|@ zw0(^>R!%XU(O>5_ZAD4oKHA%7pw%#vP>yW%^leeSyFl1|aa*R-Ovq*h&(Uc?VKtW9 zq-E-xGd<-o`@4rL@@*|00z)lESoyYi`)$=VEkBYv^+KXb&GZwgz|v0lTyy4NJ0i1X zZ56*33FBPu54DEi4LASSL+Yq(cIud5I3HGIaJe$@xjG=?NE~{d^;yf7lQaM zvHSz+uQ{$(x`#|-u)=}OHMa))(6xv>Lg*EAG}!n?i)vd8mJ4L4JpKt$(QEUyz`^cf z?w+lmwXGbz?cu~KhdPpXQjL}I39@iJ6P&7SR~xo>+6k`HOr}>s@tb6ZBq7JV^o#v` zy}yUmnm$7rEi7;AuStf%JZHlhi?-lYSns+fi`;)(_+{f9_1pLkafD6q-RoUd@kgL2Zgm!;u)lHKUQfD<2Ohu&9W{u``5JnMud~;ZWLEUgz7yFK~ zKFTz|)O3^CKi%LvW?!z`&|W-jwo}8EaYIltvtq8;oO%1LOyG20n^%N7tD!o(H_Xc@ zd&}I>9AwZrWG*Bfdxyj^Gm~K7E@jswxgiXq8sc4l3s=cZ*MCn$_0+r#-y`wxyU>Ez zQzZMruFpp{FGk(aL)LI^$ZS7=MrhT7hZRVD(cUq$Dg%H$$Ra!n=x@{~Q~?~!Ky}O94=UwS5=DNjjmvi#R@5R<>~-|T zQpS=D^Ed9=660`9RFWuAi_)M@ktRsDqUQJ>giEOsk~m_}N1s-r0Yszzy3=^x{w;*O z{ai@9;QD2@$Moes+nwlkMEEs;$lJni>$f`EYyFGVf4c2~UVr($LW}0p`~Te)nB(u0 z6aE#6X5RmE1LpjnzX1Oy^d24azoPekm447z{u$_hJI=q)QutR&t+R1*{>N3Ho&A5j z+S;iuqW0&d8nSbpwxf3eL`XQa+{NY&<9uP7v-TfNUS4e*Ex*iR>~n7NO-8l5c=&9L z`sP>Z;JcibY$fYk6!nWl`MrVVLa;!aCHln%^6~f6Zt%uQX;9gh>i#5pk^O=t_tNsp z$p1oU`zGGYNbH`A|7rDQL7sZr_OSYCB^nAf)&?!Ela15YVTHm|OfI^B1V)1QJzbB( z0ERuoTX82EUjV77|Eby%%ZeImIWdMyC zXf15_<#}UgwIfCmN(PHByaa8q3)^N8ts3A8!rJeG^+b{m55)^RRmWF?3t5Fi%blHj zj#gEtPwB+L7B!ng+yRP&p`%yNE(muQS0d7hs6?Y-y-3_7@&}8{2)b(A3FL5c0+MDX zLDH1i*zZ(=OG4@W#U1d_s{n2f*M}TWSJy+^1A(6rqtDjzN8i!uO1kF=^N~Wv3wkFut)+Q*p#@fs;u&kw>CaU}c`bQ5H-)7;l zq>HV-&nuLIk3K6UG&Y0#G)S6=J+q?fMeweni&#i(mIuoA%g@8gxZ@q0 zE=c&a@~5RsC@rxim$X+Dmb1l*r_O4oS_)B!)b zFuRTz4+!zcD+gU*GEBM;Nuc;>b5b+Y^)+;KbRbf*>9l!Mz^)fPXgJ1FPRf9uoFFWV zW*R%n(&~$$Lrq^zEj>+5O+7s=os{%cYDr`fqzZha19skgBV1JuM^xF7(o)M~UP}nx z6mgYfwhEg^QBsL3f^R>Y+|3~Go6%?NyR^F6y|k*KudS(-k)BFQfg7AND?edygAWVG zE7X%ybwtuTnJW;KtK6-rp{2R9xVpT))VH*>zO189E2)Z$p(LBoNEOIFHc^2;JPZWn zZm{tt%s8Tslpd!;)i!ka*j06OG_DL#S1_*P$C zU)$ni=i2M&DlL&?X5K|~ba-TDEhBJL3d4^$&H;zqD2gJxSq2gKI3pt^r|1B5xw&~d zHD*I&b%5|$M8T>;0+N$30tlv^n`LFzHs@y)Oq6kA!p9OVKVGf$(db+A(qj#=}H=SA!Xfu!x zFqW1Cp!h_t&e}3uY&Tl@?R|4*;bs@s=JfmqMR{?i#S_ges4`x<;@G-1xp^z~V8=pY z9E4hZ?r!U0{8b=&rAcsD;+micbyVZ%taqR4>YSZ!-eoMoWA2Y>=kPuEI=Vkxh`*UU zm=v+)flAf?%#-*^PPR!2o&PhP;)E5AaG(gv^t+JX=do-3pS2vW#KZk|$eIMc6WcUtq-CV)Q)hT#BAUH)LKi3& zA(+{kI(nu09{iFo+SbUPP?WlEOko+$FXZZIY;K1a=W!0*E6osw$q8sX9pkEZKdO^H zk)iIH5RdK2KdiYoE&@#MLWE79d$w^8#s_95B(CTu7Am19r@GF#%d+_8@x$MmG)a;B zf7WqO3MH%q{mBI?(^VVI__Ylb6e6S zWQXg}a?3@O-|!RS=45Sf8JI=cofuP|J@?0~um$|+VN;4d2O~%hO-di52mo6BlaZ4L zY%5wTt4jiaU~?}p_^`I~{`U0bL=IZ?TM-5F0g`ufWKJT2aletD?3GJ@`E8D1!T#pE zc!EJ_Qfg{(A5Y~lGK#cM18;T``T#*xtSn7WQ+J*C7CAY$lWYR+5?V0(wy+ZT@978S z|uq`hw<({%~3QyzPSIrBvFAAO<31X})-^b`! zI9R%_AX9`SzL6tZcQ2g}0kE0MKCzb*_O3M4suS2Y0V3;Rl_|zJ!nYG}q=BgV?RnKR z_$7@{A->XBa3-$Nn1-lE>$yX1Ez!nuI+9*kEu(z-&LXCZ`_v3&x|uP*CbBszI$jmF zlu-qsvrcd3`rIgbH*xCsAa!XYQ4*E*`DfXCA2R|FmC4#C!y@2_g)t^_`y&sB;t{?y zAN+ckh>01|9LEDd#2hNe@aNV8&x? z(---PW~LUu&uVs`qS(jC^kSQv+?4cE0AAdFht67kf&*CS8=gOl(=PG#!|ifTB(~SJg@r`4 zHH+UbEP?>u!8PTHQhV1cWC)m7YTroG`sLVBC@6sfg{j#O-d|}|2{j>-9OWC;TdRp1bO`)pC2Z2!*~%i3{#90V%RLK!3^)JKrLU^>S0mx?9aolSrnU zJuMEzg9?P)1mNWYW@V&goOni<6F3GBzxeqg5`K{PgNt#c*o-?qJeod9TuZ8#=;!Cm zW+%;vyoU3Ki~Uy4GE5_zk>X4+LBxtuj)36(Ig{DBoM~L)vW6+OWCCqB=uUmlFctU~ zDmB4b`Ma5uXSmCU)f7cNdQb)fxjTjFDK(3_T&xSM*P${mA84{SM=TAIcDh&e8=0eQ zDb=@0aa7?>n!OufrcRQiSHDz<#`8tk`}K55jc@drq+LdO(n#^YK1H#h#mI+?o%01b zBltM?w_uFQvYZQ~m&!WUu@Lwa@bA?j?(75yaQgbdHcBqH;=ElDOH_}*twk-IG&EoU zaI*fYbEFd-r+0=K)b_plD27KXf0g2mUR_r#lDUozQhc1gb8WIgQg zJj47PAYm}7ovoTH^-0wc>7wN^eBY6eoXy33e_A>><|u!BpF9fndgmkdGc-(mjwrl^ zm}WQv0>BW|`Yj^~sC-x36ugN6P%_n9(VYKaFRA~GLV-eF{bBZJsHv|j0e%dx1Tqne zC)ElR`MQ(AnQE{2cRKRV*pwBP)3CKgLjnM1YhA%kaYTubUl{yBA&~t{OhRzw&)>8B zRTpmopEvAPIxqymnU6e>H4wGg6jaO00}n9K^Ou^Xf7wz8^KQB&5@O~R1F>z*jy&Pf zE+~qDVN35`c|kN7{4p;QmxlNy)0ZVchGezwSDl{r5~j+CC&%em5uf1MS84oO_Xo$X ztzbS-peIcf;$aGSKYe#!2hhr-s78p1db8!z+8S%SDF%jUQr%CufUfUA&dOwNl21Zo>vBdyJ*z8fN)Ya z9{IZBninW!@E8WLd*?Dm!SMR|i;uSjAm}T7WyHPx?mI9=ar75nZ)9OJv`Kfdl6LF` z?Gjd2{SDWHV-gI-&#zPNg^3>Jo*K;0P2Z2z=e6q$_K$>f$&GKC;mDgh1OQ1CYGqjl zKvdVs55q1d>J;0!Df7pkZ42Ow8@LwC-@>L%(MAXhl?}Z&{D+Q*XN;v3dVz>H6iMp3 z&jSnj70UKg4wXhmc9<`movVT7d zJDRX+_VZD=yc)_vEdWeBd~Ha~z7abZZevLuy3)s#I_Tm6903XE_Q`}VS9h{mJ-zB% z*VDC4{1Jvn4KpDZE~`t`u~9FubHM)2QJOGJF#=W<7?l zezPR?$g1Av9k076WhFnfmI@EtxZz>lw23@;pYTb(*0=@r(aVC#r>{URr_|!#@h9Lb z0Jgh&cjTt$3`2LQ?_=AG0{07_&KaIikw{w$8!O(rF^i~A6OA6P-gP0xuvR503eJ~jVb{(POgs2BhiJ>es(vPU7koW$?ssFLzR^+V@?+wMeUbk7 z9d8KzSy%ygtrYGl(33g#sJ4Ayi=P5meJ2WF-M$f=LKE>HRB9*ukdV~sqK%mc@SbX( zgmvnAd`$Hb{t}cF4zuo9&&5Iaj-E1iK(J%Q=x07+0K*H{xbXh)RkG6x_|CX=Qt@~Y zb_Kd@VGreeEt;Gp%m^_yKcsTx$g6->CA-DzHJHcs^TADD7)-?D{{1GI3{(0LE+!e3 zwV-HBU95Qv90luVuZ*Ua7r1t!gMhmSl$Pq}UU7-oUs!#y@UcaR0#GW;`KNC!PPe=~ zUolB|7qbo4`j_P*=+PDwyK_@N*AhHCIRlnkfrmf{VbedShj9MqmIGPbG6j6Vf;Wj} zib#k2>I8*lEX_Mca_r>E0OXHTS&ad6cOkXfVq#jo1C^qD~y*sYpoQFCCz&Tb=(xh$j?gf5a*UOPD!A7YpcIfS) zh1gjV4fZKW7xE~hBbRc0^as^0F4F;r>Y268zl3q0QefOr1x%^m-nH6vZtv+^J0bSd z_E4suJjZ!kVH4J%AM4}CNTWt*`+idzkMh%P()~OubwE44_Az&XhGC!$;N`>z8<@QAnd)qUqVVz9r zXOGGO@tAky(jYU{p-WE(!;jPKQJ2JIc|0ynYG#IJGn5t2n!(0-7cdFQJv6XXLvDzN zliyaxZa|F^T$ouBgP`Y$6C(LRSrT1#UP_rSKToArF>g}GN=5bNqscCeuHa4SD)EF< z?i5A??AEEIIG;&rtG!8BIjIAH~7(U1O<;}edY>G{D6p{oPQ1~ix zkG>vcm~yV|G)K8DFAe+3}KJ!y9+UUEY1ckr_I)3!_ z7nXa-{&hG3WAX9Ia6hGQW)yPHXEY8EggF(&?cEIayQGM1vu25A!pGcJH{H|CAI4)$ z)ri|qzLmzL+p%&-!6>gpjjWP97=|S?Bqle9EIaSBiQnT{(S>b(uaV0EBI4^<<~`wq3wq3vb2$$E z8SN?d&D)c>39tw}W#q~%eFm!3q1Ku$m1Vv~h`J^0HtY12iCs)V#OrSnZeRHN^_`*x zx1>W)shJ{9tfH1t*x@AoH03y&PcK-&q?PUEs#Fdh=J{$@M!? zPBtD1r1a!>g)~%h0pGS(p)!}fp%k`_>O@Ri*5>?g-p%`L4RxKg$L@gM((Z+X>k!q3 zd47G@BqR#VA*%N+%^|>jUxWJ=MuNnsrP}IQ#)@eZ%hRwe(8h7|U3*KjY(ul&k6b{~ zFB1&iPYU5wMPlA&!A9yjxt+Ao1FZQsB+aMPoy6UG)F%qR2IEPv@VbLBv>Xt6U`nBv zyAYzcaXYdl2n(&=3E2Dyh}6?q&afPRPaQ&m6Qc;A7E#-$6hDUWAvIEP<{Rv^hY{4y z>}mr~7$*CW&ypO;co{^R_hg^zw7a1hdk_dIH)6e!H)Y?o#n0w&HXi>QEc))3eu)+s{KJuW6+Et9I0wiN69(hB+hxE(Gu2Cb6 zGVdP##PsTTDV!!CqaWY-JtlouYz%4M>|!Ag|3`_72Y#m6?ck21u3UX4$636k2=a&9--R3Rdh*^2F@$XZiM9k#NojuD*e^L48MUr!Bq58d zg&s6**yC(4jnK=8h$;8qO#E;FFeE}_WqacsS2vLf=*46j6n(PW8l+;Up+1Pyr-=Ts zw`yi{+{5_eYk_o_Fs-PCaf!sHG$rjqsgVmW*V2!a(mo8oKHorU%^=p}{bTRWhkGcb z8wX8MOu?F)Crs~N_ZOmA@f~l^u4jsiZBziI4F{OmyEOm`r<_0nW#|G`8I;iw+f| zoc}BA{2Ju<3PUJ{ApN&7`M*yc{Fn4kAd08b#^$DGPL%B2|I_5V|Ehm7Ne2ft6|Hmh zwm0?E(lcDi! zGrj>7I3$fs-&}G5v#GeTrCpktwwZDFib}9!ebHg|Gnz7^PUb~i&)~072 zJhq|{0Od#f7No{W65(E|R<1L|-CqBMDQk1FlGnh{5H*&@WjR(Q>FbM+tWl?3+aLUe zerzM*DTdF6Wn+g2)0TXV`{Z|ddFI-}$1=!c;v=WbFD)%C#|K-Yji3*Wec#1o+6mqa z-Y9D@=V@uPd4530yT!vm!m7f7zKv?r?(im)G2=1iQ*>~oebwn>hRxV(=(E+X@gQrG z*Aw99=jY?IUTw6;g@G%R@O7d&lomwf&g7yWaf?rnzSsP zwdn#uAt6P?dXPq9Fa%=&z#@OcSVaX(SyO(#prV67*0${cI62#0;6=?&t8-!UiLQf8 z+P21nBlYxfx4v&>WdKPZGWUMI=IIT01gxxz>|Pw&?Nd{JWJS6`YCrJnqI~-Ex)nbh zq6Bfyg)iW2)q-vrfKWsdfE_e^e5>P>y-e1fByhT%A*)Ir2#xlftK9| znFcRy0|Vde?8*s(c+c8WjtP76?d&4n^LKl2`{>5c2vSAzI}P;5rxcCM??US^P%$^E zX{f1jpkdUgv9PO+q+DG!%*>>fCVz|gB&{ZpZtUzder>BR)+huQm2I6PE0Bb@ZPS^m z-+Gk#Y&zEncuGm^jQ=_~NJ&{-?D$|P8SvZvt;lQnNn`01oz&xv z=VpfXX98iXOQa&p?k`ULb&qK_O59D&Zm!iEqEugj*l|-+)%F+2VPP^uLkxe-%Fa$f zZ*L&Daq-|_Fcef3OX<8i(lho0Z^Pid!oZkf6F!d=F9o}d9pbXms$iSf(D@`NH1nH_ zn2N(NPjRvNAu(PHWNPqXthC*=ERJ{>hGdYx-H(TYlpIzU()+=5^oVL1&i31NO^a9! zC-2@imP^{PQV6GQ0I`+X?8Al_ip_~zI5_T1j7`s1lEvd9ARC6RGwXCEIWx1Khr`k3 zqZTAH(@;n5vjp#$y)Xr9#FpnOC+lYd8WXHspx#x zh;0z&5pZ-_t|$Pf41!`$U;6R(x9Kvuqo~@efc;4NL$cszbf>|$U9kRQG~E#j2}`;6vA ztcb8JW-|d#Ft7C%Du(?VU|j(S$z_wQkLLp~fIkavVvO~DIp>p&m4(onb75QCwhn!t z+aKEj)#6-{-}WrGa=2IcvFBoVI`2tG1t7!^;GImq!$TzB>dyqjh-V}xHet7?x+GQ& zHtulZv^JKN6jlITjZ|e&?y0txoIY(s(pQX{F$2J-LWa7KUi24B8EiEz?*H+9R0Ugc`kL>3CWy zW~fEjXB2qk_#KQi0+qi`luqNeqsGclGj-S6yq?t&5PXR88pENWA!z|C-bAF0(;Evb zYjY4hb=CDr`ZTvDuk|ES_d*%ylx>J%ZzQj2fKA^MMQfbdhHDL3wu+xtT3!vz@sYi2 zewJ^G-Q6WWAO=_Hk&qLUwM9cZ#U>&nBW`#8ZkF>peYZx)zpSMYzxCxO;@!EtEc>6A zqi}nC(6tAV6n`^r$;-^(87D>qi2Ml7%J7dLPIJe38f~h`YJcg<1;!m4@$?VauD1q3 zK{H}Ex;c72^)KGU0du=v+OsMgO{Fg$(uU*xGXyz%d(1+wzN1t!JWZOe=hIPe-qRTe z={}dQOu;w z&7Krjnkvp85i6r3qZC)Oo-WR2XJ-Y&AOCuHR&S79?goZdaJP+U)Da&$&iNN^v-=U2 zRZ$$Ai+Uxejk1~d(!?;m2zV`~zo4w)Ps@il6$DDnNDjZ`g&#d^%a@bswmkO48g zPfbOf*#Y;0Z!80nJy_}*Kv=9p6AFrZwS|?0x?v;{yn=HX=VN%=@B3bGbZPfzs!JHo?O_ZB_%Q$E+t;V|47@LPh zb&u`oR5alXr2#x;>MCDMBSEsTJw^>0pe6bV>9b^3 zedX4V5H|^_%92&T-Sy&WDM3^1X?_vZNoy3k@a4yj@y{{lEt1m(X4lm2;HxhvWSoLt zFk;k(2^=-3pGspq7d&spY+ol2z^5%Un5Nl69vAU*FGG>p*pyWTa7=k|4wZWCuD4FN z!0WY@?R@d)Ho|dkAD4pWI!XZ=K5pu_odXHcX~$NF?8fWIVm%kT=qZcu zk48s$!7lXE(VYu=UI)VYJkLyYP>OU8!+F0PaxuK@O!asq;2JF;Jg_)VYStEZ*MyQt z$%oure|BW+xeqH$CnV!^=|5dg(EhCHas64_bYhp{qif&m+Ng(~n`H~hfygUt^^b`-JFPO{o`4k?y>f)1eb;yL~ zshhQ}9v0IHHe|oXVRi_#q_LuomEyUt*Ycd}ha4$#W}mVrB~qMUA^Ax9cT{`+-uTOd zB3N6G-KumA-FThSP;DOP&i*BQUD?{!&q2{H2i(8?>*>&X+74?b8$SU9Ok@R!g*=V? zlkk?i&84Wi%0bm2dXY_7n2zV$;9I=qUUT`>MmE5t)LoG0Y@JRa*i9QP`!%=lVloTO z#o_ziE#}rjMa`9LX@@mn&HiGVvK**76O;9l+yHu=0KG!aNE~ceeft?u@~hW_nR__F zMVBn0fC7)Vwr*PX7^^<~y+=z6{{@=UBVZ@T%^R{c2Ovu7H+tS)<^SIlI9l-0mF88&^nt*H7 zbrToMN-rdNjx)pR^aQwK*8|#b4el`IU50{@l{Xl-?*V1&3G$#9kz8br&nKXB8Se!( zeg%JgROHMc2NMOKGcpOUb|wc|@va36d*9{)WuKQ~Dd19k55XD03G$gJUg0a?ad&Xf zZqOq*4wLxo@PRlOF?ezWNPJGLkR0@C#0v`W{fDd2;i|nWp-hZb)F6czS^z5_{Z29 z_q_Tmh6>+f(u4L*d41-x)@5^xZnK2;aI;_AF!z&9@B2!9c&$92I((ssU-~`0lCsQM z>V8hL5_&(4-1gfxPqFHCfjV-$zzGpJ;I}=85ou2?HjqO{U&8y2Yi!v_-g@7oQm4(^ zavBT+=hd^1-lo}T#s_Bg_5A>?)oUI+gSvH%A3CJHP^ot)xH0b(nQi_?_=MoV^8Wp0 zcW@=Ja=GJ~+TpAVg*~Ia_cjaokXHU5;yzJ96IE0(ypw^nA`A@si~{K-NM2ON zAJU3au0RFS38NLtCmcvC_^;GI{`uGcO!z;`?4Ql`k4gE*kp6Rg{Bvmj<0|~)ApPSO z{^N)LbNl$`&h;dTkWZBtgg*Xxn=4ewW7 zDyYP39(n_!mziV23G#{6?AGtkM?coDtmOwR7*QPmghGQ69w6oX95hTVYc2ZJN7}(= zOpg|Q@7Y(q^Gk~q4UmYlx`HXJEsjqPDVn_R|+n+pI=sNZN2Xg z@rhqR>pNq|;?IJa*78zIAT)Ns+sT2M@A=Q&!K~WV(!85;@zRGCz zd<6?@`*qwGHBgn_ScaBq=`n!Qd6+)99UZou}t zEOYNmFIN#e2`h(wkKmYS62(*D52WF;&e%HkG{xvBB#aMP%RYHP za-l-t61*`NI=%LNTjJEkpGZ`;w;69gxK+N(hw@|R_2XL7rPH4f)C}ma022HXaUqcW zT5q_BsJ(0G2l@tfLSmM#lGfVfqI<>TB_>TF@YU-+Eqgg#JN%-kPY4OeuTu(X^xm57 z>1O0LEjZ@MuIW+7(n$Qfxu~_G7Q zNp0}vWos!R^{@q(Cm8Xed0D$%Km}4x??)058Tros>DiVvhv(NGbq%x)L!zwa{~e^H z?Wv5Ce=_rg_t$4B;OULhD4FnYr348>4ATeqE=>|{l-7lmN(2y3yi2U-f zdqrQ1Sro}Ca-AhSP2s9&zp)^7vBfO#s*SH%7! z=$pved;iNVeID;23Db6K2V7|jK?t~D-mbE?23;X#x64|BF_RA&g^Hr*)d+Uamhc-m zoL1CW8|0&pPv^@DS*yn$y?IG&TGqeLm{hvx&z6~+SsgZ-l`%Ncyo7Swr)}- zo2F@-bwR>;=T*eA!+p_7{lRH2`ZL$dtwUeiPV<7b+rv+AvASbC!OfsSmoZb;$u#z1 z@h9Mks>;*c@j|5!`?e_@5)%V-gjvBVBhJ&|i8wj%y0z%8d&xw|ENuZu_0%~4roN=+ z@cc2R_42+ZB&K7R{bwNI#qaU6zoPkcMTMp&*#M81t-g`u%6A8q)FK8$$p0s#zm$4d zJfv;Z1Do7@=rLFqoUBoy@^r|b(H`154=#Cz(MBOwAlza(^GwD05&KDMY*!(1<^8%;DnodNRVfB6{g#f2e7**FCTzqMV<@eFfD;>?b5r(ePhF!Y+XIS24v-FG{tXKm z2N-k>NL6B76VtS|H;-OgGI&lFl;X8xWZ#wE>c;{{hU_5G?tBwz2{{LMXK$GwY`v`4 zI^?a+nRp!@F{IgUoU&;&BAr@rCZ-f)u^V@1@tX^k=z3>O29cnW_@|TWos`g65q*BM z=`Z0Iou*k73E*yUji5f^bfumqfxVV$_9 zaIn-szpaAYY7<8o1QNt)bTl>G+JjV622FPEu2+n6Fh>m>m5fASe!H+#Z!Ji%1dFLN z6ateZS2a|-^R`@0hZx=!mlX?p*X;hcK}IbF^2*9$CWgVKT1KJwqdF?1T%LZVcxgb- z)S7-B)z;sanj8$)=Pvpw1PFPG}xa--&JaC53v6K)3`o)V`^6LH^b_N-u}V}8z79PtOH>MeLw0XBY`$KC2)(q%%IX;Pza{7HSBs zto~U`hiUH~#Ldpa$IQXg@o(5TuYmXO{3}m&br%;*NmhvU5b@PLK07<&-&nFwdKOw& zQdj+=mXV#FnnX${!#b|@v9Y>f^i?9kJ2r_$b(AY8xG_RNw+Jo-l9yDFuqFBP$6Wo* ziR;zjZ;sPbvVDGramXe>fm^J1Bs)DdT~^?!-MFcr`|(rqt*iSdPbb$8x&@6blSn+t ziIQkVTtO3QfL>j}*5`vN;!Kh7{T(tUhDvr);VIXW;-+0EOUo6V4ADP;Pl{t%dHs!( ztm8~^KW^~G7YM(=TvA3+CbZC~Fq=p4G?#gm2#8t%{0p?5_ikHXA6kmKOH@(=FmrhL zh$@@I^Xa)101qy3H@LX70N+cHA@-fh?r3(XzyDl;RCw90pyGFPNnYJA10QFTH>l*p z0^#Yk34$@8IYxqJ#+~dj9$VE>s&NmLwW8Rq|91`tvclcF*8CJ2&$x8F8BnE}e6^-! zL-ZY<3cwdXMt1ebZ+gak5$~_|GMbXca~+TzZ48PE$g28*L@K1Cs{4FiG7PD?z195$ z%ctov?M&NWy$Ssm!eTl;F$=6Lz;2)XGQ6spz-_;-D%bb_&WHhY$=IzcgUu%{;U`j) zxOPkNo#+)pMD+B$MRSP}{~d6JR4v~n8yp)N zA70JON{h#%Ai>8bAU!Ze8kglI<50gKXdgt{m&{;6b5qQH#0%l;3lQt3$ur!-Nl;ap zVRtq)IU5@uUvFRyp(cdzfaH7yR~lFa1T1Y$_Rb>fW)pXM8&iITf?{*{y2uX5`xceW z0$EKOB`?0ThHK;9UEkhdqM@M2EE1EEB*!LZT*_NmXt^Ww*+6V)5Y$RiR#Mu7{3iz$ zF&(RG2;XTk2U4EfR#8hiLmI-PIZU}n_V>B?X`k8cfxC3gI3iy``t8rK9UjSeyD&2cNU!x#7Xpt?>e5+@C}MWh00cG?=wuhcc37 z_UF;8wxBtNgq zk%Vs}@PE66Yd~rTBk=?`+Ar_uuHGJhCm|)};ZN8m*JEnZ7ctoUeJUy{+SNrx^;Q4| zE-4GDn1EDn+IoY4h-h=b-NP+*W1NJHjJu2jBbA^_*f=oVV_3V-oLGM%+SbATJIzpA zD@4(t;o<)N5i#}%VpR$L3uCXKA-!~j?$$%FeA9D~lO9ZuVspY!#!JR?*eYYR>a@BP zMAy_3Qk1+vV2hL419{(Slm^Rtr)_;BBQrgjg$)t4hdh`duF`wzj7nvp-IO6Ic}IL6 zJbjsTl#Sxp?b~S1L7uqXiuAQr1^@ss1`>mq3Y;plJyDaEmKJMg;}#j)W>L+K=yRn= z=$BM~pI^Mz+nfXD6dMNTn+FHIX#y>=2T6;Okdu)1gK1i6LCVA{L5JV1ZQ4d% z4k-9?$zK~Un`9d>V0|imdGn3W^6X$|r_P?H2|}tl#-d!4=2w~M$~q}Le6A-ur9Xds zK4c7U`lXR%zYbw3ejPB6PuMNsa~m&A_;cp`8^v?Np?1x3qEpWyTqgsVdUz+1$YZTXCY))HAqiQ~B zo8OP+>zp00IUDDg4vvXeOhp<))hK^lUIR1JH;tY)u2=4trq|pde7hg!@r{gtnL*u8 z;$V6tjRfD^b#*+5bV*gJEn&3qJuB}*#-54ui#TL>mSC%W?$*1jvS?e7@WoPS%6hy7pC3g&|-==}dgiH{8K zG(-C9ONj;!>i2ua4}Uwxzb(b{myGa#mnF{51u4Jt?}KFH{hw!v>)vVOFA&J<qQm1!M$r!4z0^0{rZy4E1$xX8p6`sDP)zPT ze!E>h&EVsxoyQaorTZun=}QF724e`%;1kKve*tv!)ef7AQBhw3e6mQx1^^Q^c2-wX{H3q9SnPIfP0uoJj5Oq0j_j z6&Lvq;B@w%?omJJreuAH)S*gd$yk{33s???j(|dkb%ScRL%J-M+H(VG`BuPgLX}rm z+8P)bn3!T+{?$?6??w0hGZ6gJIB430i8$^d%UR><${cK}m z!PP`qNJrN%9ZZ$yOJs*Ff?Nfqgy@O=l7FClyPW8v>*Xbev1+nM@Ih9ValCBKECVqq zDamedb#-(WPgDd63t9~(jT7M>L&*y_M>o)NBVbT*LM7l*gV>-OX;^sg+ROOFAapyrDWX$kKz-(SszK1+yocnMq+og@UwUH@n98`To zX?l8kOG^lZh5mr9=i1oPL#0k=D$&(7P9SLzjUxi}3(5?}?xymM_j@mvyU*Z#)&mkE z=vMr%E>6z&j<6=I#j_9+x)dGO)y2ha(x*0nAVi}lGZUieGo}pv;9drWaZy_HL?#;3 z4N*5?=cWZ=M2}bavfSLp-9JE%GB`K{1ms>ASXh{+=jU(i6uq5mV|_A)ww6&)M8ty^ z`7dm*Uu1-7DHzv@(F9K&Q|>+%?j_B`t&jD{9?@LgL0k|FWOT?09fK7KA<_p02gAX` zgCB=me`cLkjN?I}sv3!V$%!zdbx-zrYH?`hB zz1k~H@f>p>b5Zo(Q|%$ey*bfC?EGwZ`!9Eoj3hfH!-0*Nh6Cd8h1AMgaE*tYKxo;K z*AR!SAn=bvrUWN)bhx);U}>yyzc7`Q+%bLj8Yl0?DU|LWxIQ%}{vH(xBO>yTP>t-w z==cystA^<>>6~DELPC530*KGJ_szKQCmnsiq$-qW>uLrqz`M+6_GNfRvEJv1ldF9k zUQRH_#o_Au0uv=FQbxKxCGJOpy3%JAEeI)F^NX%74OVGINlDo|Rz?Pv(TTAkIoVvO zK)Atzv%rnfMWyp@KIZp6P95=krbZO16j7D7W@1hB*ImFVdl^Cw2M5$}*%8K4ON-+p zk~btI>1koN?51FvCJ+g(&v+uFpD<{`o~^ULn_Kbxv^F|gk; z4@<$n?UM{)d&1x*C?xXneE2cyo%A%La|w}dv8Ej-4;13QFx>m;Z|GZTY1K5nygs$` zy{)B1%BIbN{r<+Ixzgpk?y;GrwYkNw@``uNqazB6VW!|U2wk4-<$63@{Eo5Qn;Sf? zbb0gZ`ZROx=S!DJcG3@Rjp0s6kU{VK@bsL5h}0jwbYn0ox62Pg-94+Yv{p~Y_PvaM z%0TO;cz5OWV^K48>L}!_2^5UQNkzf6{z3gcSUTxI0x{ zZNC@@75PNe!p6_exwx^iw&Y#k)LvLq;0uxpdhqbz6)<=l_({=ozH{`7{kX=_kumr2 zQl8n0ROtaack0(u>(*sjvkADe7Sl{lO&L*SxZwF2sL!E zcXqV=kI>ShH5CybJB0o(z#+)xZR84(mDzqDBFxN2$J9A6Lp&4Htp8Y6bA;%r_Q>Ci zaNwErRFzV2EgNJkvN1o$@qwM2PmrUBEp3LB79Q_DJSyKBA}IS z!6m8M+g@#APudOjMhL_JTN|;Mq}#D9H5X z@X_+}!-L@0#fOLG<>jNxwZ4_BS|0j=V82(3?cMOL)yN%B z>|Rx0jqp^fx-IUdTOO78GTRtRNgPa3LLne*@sgnCopF^>XpHHYZLvX53E-wJ^^M<{ z6K3~zJR84UM(DH^URzpPTk&AQgVa=CS#$gSyfZbw_keOK{N1RkXd7iZJ3WaAe=Fpu zXsNO^|0kXhg7C%z28+(=KA!TWM{s|kiR}jbgA^3z&f_C%kP+l7OLzmaf}b{D+J5xi zr-n_vRB#sv3v)2g&FY6|R{ku;P7>mNj3#>y9||#^_~KIpU+leQY#jaj?zd)+nPO&+nPH7F#mvl{n3)|@?3nGC8DeH;W@ct) zW_!1O&Z&BO)L+$qU$j-FXEiTYqpwDD&&TNSbdmNZ*iS=htw8o5;e=*s+#7QbyBO3V5R4qGnljvlr!JKWpG#3N3M z)6>-QtFN^)H8$VASUKoDxA1ypHs~Z7gQ{PNPkgi=?;^*_S)aWF$uU1Td^V{oAIIEo zf?4UtEfdqzAN+K^YbVP3baM>_dG#_6+Nd4Wc~rj`lh1jnT+qNVFEHHw&;oufTNM31 z%i?u;YluGgoUk^Tv@nYF((8<&{U&7nzF{;6x_Md1v+!d66yxO&*3*1g9YiIG@k<0n za;kzOyHCp_)%rD`tsaba1Jm@0Y`1~7i=*9xiAe-+OLvrJ2T6;1$7cj)_24Z8DlpR& zcpwlg1{i4l`KI#X)<{x)LPBz4Yg6`RxLJyT4Hy-d%t`peefL>h*F6+{%6ATLRh-PPyLBj5_%IeB{>9$kp33dG3|a&COC@ z<>H+8nvqb}GZM0J3dQ~RaOx$x!R^sN=@5(n@|!o2+y+Cuk!&d41HEeMtGy2w`ryqs z_iNTTT28N_y`3oDZ=EL}XA$x)4`-b_E&RkA3JOFra2FqtqZxRu{yl<9gFT788_JdafNM)(GrU~t*@zz;+2=v#q&^e*Xjd*~YXT|LF9T6w5)0Y3#_HqCzV z>-cY4?}{ixqIfxb`oD9=&=-g#!JA4~f!jWO**|kF3ip z8Hc`^)4bnh2lAf^@V=%xPnGffmRF9BzzJ3~Uw@wb^+y=|N&e#V zGSd1ErhY(%OE_v%|KaP-Day_&(atE-POIQfshp(#cg+7b+`ES<^Un7o9TR-nlsBPZ zVErE2wtiihq^)-5!|Mg%mB$adinR4COcFBP-8jNoV?yI;P*pv)IvWVrwSx_pV?9hRw zhF{kYXgY;5brOAQ2sK};xX`WCWT>S|h360!{`RXG|M0x#U7)_%;UC=PxBf?uqp(B z@1Ee>3%BMzR14SBncZ^?|UK3d;s{$tJ(}bO&_U@81V42(`q!?%@*&$S%haJH)%0P_x5m6E2 zz#*&K<{gM{uxaMqQ*Kx;dOz!dN_%Q}0ypj<+~E!IfIx3J_b+$)!&7QMwyCIa)@HO;B~NYVzY za?Ec`iw$c)<8c$!BxBIACvl!>dt!o}WSHHx^UTpzQjy*P^R9;mU`rNQ`>-ckC zJj<)~2c!bkzw-KlZAbwvl$a{(ujyQ@#?Na%6)HwBgyJdZL-llAzB_6L(eW6Dh z@N*Z{vpLY$cYE+?@Qk8lA+7`#?fz3>u9t(yJE)hYPe~4IzA`zWTq|RM7oz3+lhC0bu`*a^9} zy(2^KPiA$Ju4**0GI#yyXoL#47LA_CA||;w{r;l}R?LSy@3>%iP$eNN*(Wobcn4Wr z^JsiX=2oUO7Nl>Vu&MnK=DNA&2_5eGRj!i(jUw@_kkr!8K5J?cU5pq0#fBlg>NpG; zQmw%IhR(>+;#+-oxLCoVnsi`cJWmti*s#AZm8!6Fcu2dFGiV0LHEy^GEZ}GFtScdYfer#Myb)Qd ztgQyI^#_KcO6Q6I9Ux zXLzRd6r7E<<&8Y#cGY@-TrgUR{jH-@RLlN95POIIc%7orXtl5d`2|IeR<%z`WNz40 zmewrl70N5aIS}G4NY%)75HM+lU!>E5wn)pH8j){lai#8 zgA=S^y;@gvPD)1+on|brVmaW}FC12|Xyk+h4#++@7hebSwJe{=-j|=LkiqvI-AdGB z?2W=R0%M7DHtCqZ-E5i1CO2B7Tg2Z5r_8L2=om)Ex8hKZaAwT27l2x+P)NWqWpou* zgxe*@6~=1mV_ukoF28~dj$)}qtIyV|wY9ZQrnlCd2avkWoKBcEs(HPEK8uz04RebL zxQAwcsTB`fF~!(TJyfSD^Ohy&QLR490A2jxRg2ji5oPF^xmhG?Kp*eG1iQQODyFlL zEH@^Okm0%%-{MN&tyT0mr!4foMIeMyrXk4+mHPaZaV9h%Ao>XqqPw*}9+d0+O=A_; z&;Eu@IRK|tbMcX`oRbq9tF(jyf%nYHhAi07k$IYNUOtYJ#4s9uQ!|5MLh+MSTH>d7 zg7=tD$-4=D3-41KC4nq3EY4#XvezFd2p8v^PHNyKxp<%Xp?vz*K)4Xx=2lkXT6<>@ zm1{^vRp#ciaNim$e;0?J&Eron5%5?xS+9P0LI}f!&OH{g^WoidQV~Oe3RASCWPG%tNLwb<+&x zXQ`@dZ2i@hxQ~aS#QNmqqZy#1O*({NqgLA;%%GvIotl>2=h6JPrY)ZqjM1lwY3xs| z9TAm|Lp2~oGvLmsMiEeWAbBsA96n3VNx><cD=hTW4_8~Ek4flSYAR064WHC2 zeJQxDZJ0l%kBf%ZH?c%(0%)uqnH5k`Nd!N8@M__wBimVKXEhd<#tVum zI7Ju7WKj-2NWbtB8H;^Lr{lA$Nv6qpyBP6Oq4U!I-i(+=iPD0)iqAuWs_xkIeKOWymiL$}$yfHbPx3K2)aVyezlxHZpo2)UM|O|# z@nd#yewvx)7ZyVBst%Ef664EbtZih*WFJh$!`6~N(<};Y)m}(GtGr_Rvda?08CtyD zijd{}Z{Y@2`T6;#^#tku3<%c_4pZ6D@Te%C_Mn$C2(gZyXEeTakD~ycfy4G9`$o!ul(ew2+V zJrA`%~A0JFjhC#H?Xj?&MfXvXHmnL7i3IU`YgR(JufzATNs)e4?vPSI662zUfsocz7Cmo zbbQ2DK(YdX1}WbTcA4&PoZXyUTq&iA6W65n>fV2wzjS4&{l$MOjmvaNITKs=oE!NV5+0#JF#a9aI)VD z9>7x=wbpJrH30Y>TRV#H6is4pIOvL$UfCQ~8l59=i*RAb3$IG=d3rAmr zN%jnsKDxAZ{uP-*`bdH_rofJUdGfJguo zfA&s8Bztw)G*5rgY&&5QfBg8AqV;5gy=XGB#aXcpmkFZ{lAnX&j}pyEi$DVa7C;UM zBFNu3yR#2<#2eL?DHvg*^X=;6Elsgb(yb z1|5+e4z=K9+gN)uxJy9TLkOd0!zw~!1=eb7!$ToK>nQ=gZlKxX1y0apa(Svg(nouX z`dB`N-d$G18^cfGWuuY8e};#`g?E*Jcq8@O!troB7_(Bp1A^~K)VpWIoA z2Sh{Z$+3LZ@8%tuodn)4bqd{ve-;KbeHJDlg)RGZ^x3p2{Uy>V_!R-{pI-1zQ1bDw z96V|SUxdw*$AB>CUZ`2Q1IPRJhX`zOb^|u=jAMp=HVb;BIV@N^?A%qjK!kjIcJs&f zEBrsGB>$`cpTWPOnx1n>{zq-;KSoNiagef*{#Ugn&i_GqVE=y{^6)=LO8s}~3P`7f~YUts0Gz{-Dt zmHz@O{{>e5{{dLZ@t-q>x&Cjw0{>6$(SM+hVs_u{998U%Ow34`#m!u;Ow1%5jXV&U zrEQ(f9PRDC8#$YiIy<_UAu@|vIXfwuIf~i+u(z`{vvnr@NGtwo_5EWQDf8EV#)+Gm z*qNFk3JD=PIXjve{XlewIM=l>!IH$_Og4=Oz?7zy#EKDyHltJ&hJ=O>i1=a8lXVFK z8#F!YyWHQ6O)%DqYQOd_!2S55R&jDs0y;?hiPtkrL2kV|`I+v)O`EFQZ}Do0KDWUKsO-rX{AA1E{1mM$Uidz) zQf_Fy=mzCL{6(0F6lXZ$A?nl;Q9(6QBzbX0D z;k{!0u}=Pw2Ti*L(L}lf!W^}>0rJpI5poJrtz4PU^QP$-fAb-vFrqsWMv;P3$e9kN zNytbn1VQJ$S)t?M@r#B7-G0QnHRvA{=vWK?15@1nRp!^5|Wcfc5rDI~pE4<*?F=Ff`IB7lnO zze^p@)VWYXBXN#nF(TP10$>Q@n7oCscVc@4|IpvKk;Zt!f*`%*J^iOIu;0B&TVTEY z4B+h#ieIG83S@z%_Qzfr$dM^fEXJH_olaTXveQbraRBt09FRJc{Y8ccZFreM2gdfz zK8Z2}Uc1~AvpsW=hjW(&MuG5pcIxlCVeo5sbh?Mid+=rYLa7EA=IG?4XR}nJ;*Asn z8p^VQ*+{jITP2r+*+6;s8iHEjS#?6(i?}Yy~mc?2~Q?AkFjVsmeuzN7B zj8T!P5B;C z+2+Yb2T}Ix!ni6x3m!VFK=`28lPB&Q0^aRY&=mBPrPAP_p~|-jL3&KyV~J^c!S$1l z_1OQ2l~!IXdAn6Eb_7!Nn*)?(5**KM@rlvHRGZZK>4!Uy z1Mlea^0fJUFBK`D1b0y^~Z7)AZ5AY@m|evgt;DugIP$F2(cya&PrQGA&p zSPWnxfmHCs7d}w1VjU=nSpHI??}<_DMompq2p^C65v%zjMZJIV_aj64X^7K{?Ozf$ z%+9j83S{BGNbxa0W1X0mc}`aE7BZ(ndw zvIy}(S~EdUL6+GMm+ZW;tGXL+r287$yE~svn1(3gT}I6Qeev2>`BqAn-$&@KOg5Eb z2k%ieqyL#M8Loi6_VI#XEiI#+U+{Qt#E1?clMSiJk~`FlPOdMk)yG4X5D4WXctzKe zAZl1%Q)4A$1IlqC5hJc88o$F)Wu}0+-SfHV?hTD57W90&>JEH;H2;`Axt0T`iQLvN z`m0`t&bhkFWV_2A$iuu>b4h_U?gT4wKG)1^5gQ z6b`f$Lh(q_+SeM2Ke5U){P3=7=@|IYYZ~~~AVN$5F2;ocV?PrB;)*a(MYu{F ztczs7CN@G(0-QK^bPWtJkwy=$%LWQ3+Jgmf_^gK5=O@=Na};M@W-SQP zUtjO@w&=T(2-G5|3C^UPIGkYN`Bmu{$QkkkW6prAN?_r0w%mmJh7k1 zRwY`q3_c=eb>J>_R#zusRzIZU$%U*k#$G5(h+39_VuW9Y-rtdxErVDSe-}9<2o4LV zcGe&UQ1b-bEvt|QD`KM326gRf!^S+9#0-AN70pyd9gm+l%9&Yt;%c=7oNx9m!}x%oRg^+LyR1Jnbw;3N2v_Oe4FlYFSyqGK#d zuQay~MTzSv4~AbyH7XzAEA`vGt6OVP%(|frA*8fvp-F#g%>`n!u(R6)L$Skipym_k zAk1ip(nFM$e_<$dhZi^U8RKPfw1e^0406H`r)MtGuTF6VCpiCnY+tP0Y=dd@xP7_b z_^>c#|5+Fzl|m?TPC+s=>g`=ygTnrV6#j5mP<~E6qaiF)+yaN?Xo%bRGs=jjFC}yl zZr+I#qeNiRm26MxJ{Pm>^0JeC$?tUmrpgGTN*l1RNLAIQrJAe5o&|U7<&kfPe7rEA z^4ULBctNciaqO^*0y|*h9e>|bXVq-^i|jqJhbid!VpJphcD*(5@p9Ae=?cORb>qC# zM+|963ClZN0KlMWB%um3I?6~8!=I%^7{IGWDu?1P;lCh42KN=JDkGcXilliG8CW7j za-(kfsMvla;11rty1J4C5R*c}14cZbe=6ByhHV}5skm{LRa?9NeKY!^a1el{8HS}v zbUbeuRb1-e2j1Hau)b^@-1jZw#83?W6JCmTOLC4j5|Cv)EZsm6&n z1$=e*1!&>M5dl&?Sa-y&uToo`oUeuDS5t|;(R|GL{5lJvqM`dmH5tqAAb#4gHt@}G z06A&7Jp?QSHRP|Y{@;3C@^o8J-k`YR_-7NKWydcyDb$VjV5MdV7HDhyY%{cmq-u0y z0uef}+FyNdApK^IpzAH(b$v0!MRh~345iBZhK*$yj?4kj4>Z4Zd#I2_>K{#VTnU!| zkhn^P5n*4VIF!oR(=!KSQfYV5L$s~>-STnzhib#E@bT;e`+y~NLl*s~*>ANLlUW*( zB+V8ngZpxUC*1|{3f>=g-fKjVur8$MJ+$UCGw)5U`zd!!YmUcl=hR!gP+|rmM2s{Eb2oX3;jSUL&$9C1cYQ1G8oydts1}sLZ?YAG3#6kD&9S5 zT>)%W+b=W2Es}>xs;T|HH=B~IwJh^;hJAyM6_- z&?LM&I&>GQFuEdaU66Tp@%`g-tMnw zr|5$u^@Tpc+4Wac-g&SU2k3s$AV3onKn3{NUFfR?=rexgyQf$IV&e#R6bCe64u2>K z;W__P1w%0E1qCP?1@+;cQ0~1j_q_E`<=F&tw)PSIcKfl;TjH&kB<1{L#!Pz=^y#Nu zEl5Mc1Y}AWg$T81EF4nk)~;5Hev0dla}Qti`3UCR&&2}*W@^2dKTiy*_yzmzL<_j& zwra7|>8mo5bPUwj^sVVI@&#Y`Y;Ck<(v!!6Inmnlu?A#lvPqy>9!9_+l}o62QbFY1 zuLc6R%AUgZfREy%Aq~x}h`0Ut3PCZirv*Z_@9fhLu{U`)<9At?GnYh`xzMzb z!iFUSfj$Ohg=~@TbpAt~cb#B!R9XKI-+Ll1vDmvNp!c=a?r-;{Gt(jNE7^t4!s)T( zOq%K%(#WP)t)HRqWZ7FNa_5ksRl$leB`NoS#%$+6-TVAx=c{a6KA!DbMkc4O>4wC271aOg=9L?+ulB>0^J z!OL`_y>7o*OfHmp$u3I{ z2Q~pagCfhBumo&h%|^gk*$I0uZAotep2$B^=Lb z_vJcg@4iGbOyLCl#(=rmS$-r9Ust$Cwp(r0G3tZoM?r=){dp$&N${qpChfGhDyi)9NMs)kgQ$b9$7@&iS2vbAiEg*jM)=?Otb|0FmUo zi~DremEE5#6?&L38Ec|9yugMLgx@VQaIYa9kp@bz(#v+rEC)X0rE!x@7ULgLBsLqT zYnO80J14?5s!=yc$_-8i^(Q$Ro)w=>-$M6=YW;L?1Nh*y+Nv)`*Q@60&3vxyo)jGe z2uYzgPwXQeq*|j-iC}$>iPxOEL&HfjNg%WSynXj>T<`FXVfNcUqgf@o11Z_wSa*{= zA5MoXw9)%{PZL;1j2*i2f6o&0;zRK}V@K$3@T7#$@-Kh;r8NO4Xb?3YHR(%L%WU#sQT@RTTKWTld z@I1SznI|K>ty*VaJvg+kEHH_(d^f7)CcdqTL^)6U&ZXNo5QY8LHau%HuL(Rj$vo+eKZNsyFyfY7XAd>VOgp_8q-NuIDjp>cJxLcl-u z1^=~mem(V@J7IJEXm8dG$!#Yu0QH3(s;WVV9dHKN*&F1;Oz0Zlf1F#OTIhZbUs=H^ z1-#ql)JA4ZAK?Z9=JtDBL!s$fv~y29iVReJeA{+8m^!J}XW^gHbls8&gwGtO%Ou7E zCJPuZZA>ZP5}*&naM^UOadA0z1C){wyC~rg$(Fi02SAo}NHdT!F}B({@pj*B@h+ZI zCmx162}wBt`G7}%T>~0^&p(ZG9l0SwE}WGCZkJXWCbFmP%3W|ys7v$Kg=C<0x zP%~50A)+*eFFH3bZ6|7{4Hd7d;I)?;YMN8~S}et(0QdmKg->2H$c$^O43*ZOG z((RVuLv3x+yF9|ykGi_Nybm-C7k=OjhG5>pX=ozPLv6f=-Qm5jZNsf&zJy7J(8}d` z1XHVzp%An`2V~V^=&j8n5}9Hi9G6=_U48jMzg(68;1;iQMj-Sr{?}4}C={+q0D?WA zIcr*MR}3^iIWIX`*ulAehVHWB<@NemES~Tk;6PaJ+!Gq!=RAE+WNd*42~*&l_!IpB z=ZsY?qZr;1T_D*f29xkGj49Q01kRl`cpfA9=NQ}(0ar&{$(ByhdkIuSXr4*@Q|DBR zU!(q3a<^k9QL^t#>a&Jt@bX|yCyUQ}!_+C?i94TgG38Si4+xW>Sqkgbe#SS)^W2s& zqk2fnxTUOHOs)YHgVno+x&8RRm`%Zw9#xM1^U%1`osVXK_pWf?qZ&Q3lN8{%J9Qpfdoc?>07;u#*irWj|fG4P8mp7{F&dAp3YJFeX2 zOu_88Sr$RJu@p!Ofzb68|KnmV3Zsn(Dqv67W8KT>L$+Xrq?8aAf_B_X zRMBMA1Eg|u$#C{g=peht!i6?5WFxVUaXZ@uKpO^@$pqpLEVE#(H6ZAtLh?ch!_S@5 zr*1E>0m#rGr=VNEE~#w`%Y8OjK!LSnhrLWS=+uQwwqC3U67D3z(3Y6vUE+U@3S8NX zDnFm0pG?!}6x!gQ9nQPW3K7|pfxAsQD8qo2n22Co|Y)14viN=p-Lk{<-w;Vu(2{Pr*E zEI3Yx$~40jUpv{DU-D_P)~D4aVrVk3*)jGpbGV*j&~2^>7b+2_=V|oL4rueg*Zns$^Nt& z15MdSFBYNeQkdPlnCbdW<|(Qn%f)$^2V4w6Yb65NQXQqnbU{mJ0^$%RR(%R=tW6Ft zAx8xKiOGQyVj$$mD{8gwFy}u#I`fPF0W(m}5{E?6B@tPjeQ#fh#IL6UQ!$&gqVo-n z4Wa8VVTB2c~-&_&QEfy(91=Jb_MFFz9;D*r~5@0V+nuskuuD1H^~+2`1i2Qj^A|P zzg^Nb@GnDF7q8+f?HS-cNn!*E6FE!Tp&f)(*xqn(dhJ6hPKYiHWUSEYD|N)Fn|ppj z&ocAp(&OyypHv2^LF0WP6RZAy1WNp_Ad_X_j7%gZ1U!^;Dt?yAxamX1; zkU_%b%Yw<3t?yrTYp59+px7i~bW8A0DQx5)s(n6NnlDqAK&_XZKSwQk)PoGrlceK| zf<|NKd*&OMd8er%;vvD1>CY_#^r|cX>~3+x?{KO)hPDJIF5p-trW$tq7`Mu+Q*vby zx$j}nlzkN0CGWuK?cXARuiRE}eUI8bBz|U69gr%r;D^#q!s9OjRzwaE0iEB8lve$= z^FszHz+lKsKk-B$8+!(c>;Hv>^pCn!CjtqEdtiJa{7=SH&i|ZC^N$_%zivGJADJ!x z?*dIx;m~Q3@mLJI{QvhTQ(`z+a-_rmXtU+~&j~d=|2L}T`X6B0{|}2LhJ|$`pQ!qe zXY?P%)Xvx%Q9uBZ`J0)og|j6oD=XXo3g7+jD_BWcS=c!K3le9$wwL$p{4#LzS8r3;02rPJf zge|=Na!M(-Eo8UrXRYTx)92W6ZIjG&*e?@4PrZ}Ao#~@a6_d}IhXRMZhe;RMjV@>? z$5hBx>8lOi=+Xik_(5IKxX$%V3Nu#L$eX%Tv*+JtbaBYWY9vN+-Keatl|&OB`bX72 zEiV2JvQ%(!tz-Ij5dRx5(bZTqE~FaI+||`(GdAA^j^(?)>+R3&w>s3AiA(ENlP13# z^qGmvdKVYUQLk#hxaPM5o!0m|$uFBt-V%fM9+qE08`S>AUu(i>hvdXb6QrIR(8#?j z>M(Jf_PxCkH($mTY`>v+OV>yqoyMjK}HZWHSL!_Nn#r$x0t!G zVA369!7z5yjWSmc@1w*2U!p=oc z&^}na;oZGeahgWjiv4^QvpR(41tZs`@KvqlD$j^}iV<7k@`dKLJ`{*~5tjNt4_)yZH3ux_d^WfH?cSLJZ-G zc+PDnzpL=t;sR7z<{j2R`MWd^{ZS@I50tg8uho5{QF;$%QIc%rS5Mg{@q&<=hufz1 zX+)(RXGb@74vdU4dy`8TIbN_t`^i9>BFBWpX2RVw3nG|~U+_|g%N9H}zkQE~`ocBG z!XiC?YKM-4ewjd;n=RRRV@?qTtXD;v_BM!2atsw0`#G{g2Fx3{RO z`(pFEbK>xN(%${8UWjDiux?FA5@LXE)t^RHq zI6vrz7?~imN#9u9X85+Curu_fG^sT&MN6 z@5A}+B5(|YOX4Bo`#hdUn26+5vr>4(5m%<=)rB*<#c4%MmE|C=O3qlP;mPNRLF9+4 z#ACVf?ez!6Ce5h6^Bmd!@g(xi&#s#xT10BRyt5YNYn!#+$GNGW+~i7I|3c~6JOyOR zJ2f?~AFnk!H>YnSvu`Xsz+^ZsEuiaPToip`cG2K}yr(>g9MPw=cx)U2hI@6y>5?*(P7vEFqjD>hGYsHvpM9|*^E0jY*qh@SWiyh-xgVf7nW#{PQ2XhPCL zvcSE(3p44UF;Y8G0njHc@yx-S^*yzklrswJvRMh_w|y$4Z^di$Wy|8RCK4i?Xd4aiAy?4V_Wsj zaqi){C$nG68&hKstz+)d-5`4edqX^S1-M(Z(fHDX=)y}c{aL@j zbi`tmHBwv;D>B4nlSZ#lPsEb*+ECmg+yankRxul5s8u$+DvaSgPs3Q=r$gg=zT#VG zydt2qH|DqCRQ#}GbN%1Zzrmt8*2jBU~L&H!+r397(jf#=;U7+Yr^U=^E@pY!*q_B-YCco zW>BcA!q{VjjkpcdVpqt7xsmpS^3&^A{7+DCFkcj-N{Ryxrfgr}*ToD9>{w-!$Hz>@ zE|Zy;!wB~?w}o~Ed+dzQtM98Dcd)M&cEQ5f3>df*->|Jnx#PG9&mGwk-vE-;xSJ5^ z^_Y8{H(z(&p_Ycwp5bzZh2hj`Lp;3+wzM^&Ww)exA}*_;KX``e@Fm&W*123O;;UA; z=LMJ3`qUZ2=6_k8QXsqyEacg7laUw)|5n4gBJ%(FZrHjsc)dPxVG6h0*z9OazSuRs z?b&?425S!S_{lBRVBBsG?z!{IPokbjtVwIhzxEb(D)O&&w1-Rg8c6 zXYDm|U93<-MX)_^r#{`ZUnhBf3`_ghjD3VRNx{8CO?a8bL9E3N2}|qnD=pn<)v$g| zU^%&K1&PHWvGOlvjyKr%We{W5V*D}ju4C>%3MR!w{yhXgwXwH(H9NZ_InT~K08^|vm=*Jq}u;b zurQFbanb)mh)7O2(x$8B039Y{%kz!8v0M~JmU@)Rx<}p3MQ&+lfaicUGqoXjdmv?; zbN={?2K+3Gl{))FY*H&_gm)a=9MU=x$uDSWuTnyrv_(Sm6;Y!V`o@BBgtWDJquGqC zluk0{erqn>MEt**D-BT{EB00Q5YQUbI%Xw5pp<9R(w7+Z2&_N#t{7395+{)tvb7Ei zcF(rV;j1^5u4VYh>Y(egm3mBluA@${OXlE0)&7|X9h>Ae)Yj4yoCi$crn8G9RBsw9 zjo&7@l=}-lX(CM3$a;eAJ+A!<0N_JBs_wC$>Bb!OMUEv7>r3Jv>nMisY-2(a9$@TQ zI40+gv1GCwrmWyx{AlU>YT**syihP7i1?ec8bK|%%G)N31HuAvRMysFGpR|Zu_d&l ziLKxVPw=CZ0S2#1<`KFUg4k+-dvu$Yr~O=pxm%$?r(hVb#LAC~W=04r{Ld+eRm9v^ z1Lhh__&v~d)J}zxzjCWRNV*0Tp?7R3@>hJ}f2bY|JSS%`{9cK(J|hWh)BQH&YQzX! z)cP&BI8w87=JX)<(XcFYam{6^&J_QJPvnyWDPC2K??Aq26y2@(B2`C$mFj z&A5TvgOm8F(cg%?m^3^X(aC|`tSxe!J*~HW{&?Ha1>ORp|IyvV>4`_imNuv${40+{ zlm-(k>aA{s0Ir#duXGJ|ot|fPn4&?}nz>5tDz?zZV$udpWg{gK1REb2PdXZzBRc*K z{2iCE9`ahnT_0i7+80v>N)tXDiI+I>oN-Vk`WK>Y0-GpFTUku_Bp*rmBmj|#{Keo-&rYR)h6st&y)cKCScybijDy@Mj?v=VkZq*U~2<)3Xgp?h?we|10T zMBUcXAYHqwMDMT-vNLt$a`e)J+~(ax?^*oVm2r(=d&{6%2DHrI0->fsc6cH@$;8x2 zr`sD!)=A!n;T{`iF?j`j$5Pl#gMqiUYy`a!wx5Q(ZWoc>Y-ggkF}-=M`syGeF3s(z zYOl8R0*r`SY_=%10-c!?^#be8=&LgBlg+#SGOr^PU*|g_ThXL2kW6>(=U5&Zj0Z;U z_P%%K3T()PHCY&VCM1#PiDJ*(prpgsD2<3$<=EG=!z4TJySUBQmTJkvkU;@+FD4Oe^(D}T%Ntu| z#`jql#T`UCB|BlLXUBq4gHL6_re&0)>z89A5~};`?NgWYH}8e8I6()`z}OzO?RElR zplr9EbhxXy33gCA3_~H+;%B7G6mmw_+T>lW&Y#ui$xb2t~{5dO*A&N&zf#SL% z&dDaOG087XD2LRzK=fR8t}U1iV<1ex6Fy_^Ga;9|ip3(cSWvt~TRbm%Frk zZ1f;*sn_Ab6VMHPAWm!1bjC-AhBk$bEh>v-1%aLa$3M-1)z**;WNUd|IA?F}bT_18 zHxqiK+al!2U%PcWj21ReHRTTBpykhsl6j8{M|4`J<6NhyYL>4YXN1w8%TcPfD_yNw zR;VX<%ACXKqXrbG&-*i-+cV4%sLx_82+PcD56Qp+iOr)=7uFB>dWXfQ#tQE#Ab7AR zEHCdWKe5h~xpQ4`!!Ms}o0RClEBCcrKifCEkdN9$p|t9+?^ENFO64py?NW8Kht65Y zQI~Dt<-rt`oQg2gGj4;U)n(^ruR{e%lR$*!qro&S%qX(RH*&m^kYgT%Y{LjcqYbz( zDBmOW|5S7kr>3}VbC0!_Y!>|~i<_UDdZp!$e^5EmFt-{>FiWMo^IQ>wnG}tTW8;G@ zk|{TFpZ2dE8ZmBRDpKOZ!zNLKinJ!=`{uzLsd*l>N`H%ZgAWA7m@4VzvzSS6Di;1&8}*x39};Gz$*HRYfs%Wlb>Zb!%}4%Koya z3sFo&aJDQ*k+9{hBxhAbnNX)nTnjha-@mv;@OyTTWmJ_I9illtAfe2?fH^qKGp(f&eonf~&#`BX7BD=2Gb zv7bBP&2*ZD?2mL7Ded<;bCu(8Je!baDYI$|?=9^XTDnBBA^G>1FWtk6RMr5jabx3o z_wKwadwrAeW<1Wn6?kRj1C+!j)b%-dGqj#KW}d9mHAlm4fwV=#hqiBy{ylUEbNhE5 z^)v1nzmEo_`vgNQ@A+_hZTV}g2E&#PmNqmVg*=ffJ7&Oyud7*;147UIc*;$a9U!YS z)w8V>p65NNWZ31)pLh4QRX4J2A&7nQ0~ja~5%{)lEuz#28^spjBV>(wNH?J*-di zw6XX-v~wX4jJg5Z;fOjjy8Z5@J-Z`Lm|3_`8V<)z5G~Jr1`1#<2_Pmn?P44ea zc5rTYyNd=9k|ai`a`t`6*r>y7_52_*4ga>!R|K-8M_dj<72k{#f*dg^gJW4g-y=q^ zSi?qd7_$M-SJD#_Ul?d;{Js6v?bs5tsqsP~;{eIm+FdZ~0aKNE+*t44CqC6E)hITF zGsA1sztWUQI6~kAf=rK2JA~#sJ&f7EmtyHD&0#i z1L=%9@oH6!5s3`#?tEON6vWp2293QbZ@>O-#E%H{^J!^FniX%8Zw>Hw+xBOlx-BL( zlwNt%gyEsTdv<5Fv(NIeuG^(0o-owvvR@BQMJIWO8Y1E2wBCkQXGCV=p|_8aVTs|t z;pl#T7JC+}(Oyg)!6Z*na3 zg-|9&*nT$VjL|g&XgV-m!sDD(b*bOVJ2a-GP+K=1YzCR@ESI~O}c4>#!T)<1t&5a!nR}UkV#-pY&x}bI7jI8nuCG$~Tah#z@I7I}K ze@(u%?7&GbFUyKn4#{G5>9Te^m@1T6lYWmN8t zWYbIFg<2HI@Y+9E#!rZxB&x#OP9~9LJq0%}(tgy~<2*k)CjEr(#P$w#E!J-}2zN0l zm({99Q%~dKoH^9Yrb5EK!anLD!qRfaC1S;0A|TrNBlJA`+=t+l^{?mmbcte|I=T+k z*67+7wp*C2{+;{w)0K_5K2}ez9HPxg>ayQKu}V`vTOhE_lsTouX!8&t%BgGMg!`{>3dfX~ktbLyX^|q8AIt<)E`IMT+OS6KCJ4jGdCDpP?M1 z?OEnscI#zg+QX?+ilFvJsX$K9RC8U{8>T;bgAL`rCWhh_U`{v2@ykkW2 z6oq@(m9|^>kqnWDM-_Q&k_-dEgZJm__t^M=l- ziNvx-T^FV+Mv1s|-T1aj6xTAS+y=jSPYmo4+}^&W*DGf1%znFj`JVW*$E5;WjbbHR zAjfOcH_Uw`jhs9}A991Tqxpi-CPXIfIJWHqxiZOAYkIasS(lPiqWpSk(7ik9Lb6$H z*POi!I?kH37|vddEs{YbFw8CT`b7p!27jPWW-={ z8h>_I-`$-4h+_3UOW6(N76RqvK_M+nwFUW9!~VEITtnKc6|Xw`&9lH>IRg2T$*nim}*5L zI3cv((7Hwc7t-DWCa|Sx*Tvo4VQ_b6aCdiicXxMpcZb2+b&xoGhnzbes zmQOi6)%jh{K>{7^ngYkDhT&9ze7CNHsX=igv0WrDo0C;C%YtuTgZF1BpEXV-)iaNq z%D2YjODs`XI`Fg6_V)P_3iAMk#c`B@Zjs=m4F_btgXc(wmNfXE5fmRxvE~2doN2L0o@nhLBzyEA0tQq$Nj>*LAXEE#B)c2`9A@EHorLH;le!=V3+_|%;V3Tx#3pH1v zs0vt=Snvs61ba5=309l4&pfKZSTz>GkFgZfj8RO+h_mvUsIlAb#OKu zQ#g=c30-*5qPsvi&}UmA99ln?_KmCso);1<19kBC7o$u|YONy3XHkMx@*l!)P|TAH zSYU5RdQ#M!%n{4_%GI;wf^O6yQW1Xjyo@2wB9ogys$-`xyK6*|26k?b?KSzt?mlO- zH)u=?ObkQZt3DECg+(#DBV9d3uMuPI65|{eT{Ga&9--G}v`H za?ua;q1pnmX1T+DmFD^>Q>Y|bf?blfMPIwPG0ol{vo6y-0B2JgK0dKG0hWm9oXlS( z(G*E>BZN7BIKb%-nArC{?;{`a__y}5fjhICQ4OzUOiJoLox2SinX;#MW9N#3o(Rpi zTu%U5H~wruSVP0qDI-Ql0Bufz2{)Vsm25E(!jKsUN%>tGg4_j3w-Bf#QRqoFnJpN} z5!s+?IUtyQ&eN`zu3zC>WSt*T0aoR$e0!uNgYDZO;I*KUhVUcZZWPTY0CYSxyV@q# zvr^5wdn!kGn3aRbm%X+ppJxmPgn=z$8jTWOPxnm_(}yHau>lqek-EpFyN{0{gP*_$ zHCMMtumD@EB$xee&kyD*hSJ*ScUkQ*#@x7@l4yZ6`n{CV82mHkj=lGuC{MAnoY;1Mo{kMD_$z@{OHD+Bvzr z5jo5e)p)|ou?5&7v&%6B*rKxyqO**QskaS!DA{SbJSx{f=Kvpg?3F5MqJ8>~*dCdH!H z5CSbsm}zn%4|Jylf<_}7&{=3?_R5Rpx_LUB)5*dePaznfk7;vS$ipLL35Yq4X|!ft zyYj)ltryZvN;@XJm!XdppqYv7xj`hE(?q7w$nL$^kT278db;TL@Pl+($j{$&9~Z^~ zK5i^FmT_?KlwyrF4!LeOP=}lPf*FtR6;FEa+v84LN(bdTBdi6XoX8Lp1T@+)6A*im za3Df=HRAiU*?QfLb16vl2y?!u1zn_FA-BDq#}|fvZf#&I@sNH1W1%dM%qPGP2||?9 z-#wD!|Gtc`37pR(ub7M~U;r=%2nI+`Z&8!O-U`M8eZm9?JlYqXf%3ZtkOiuJZ|d^H z?TdA)Gk7A`@walbQv2Cnit)oHvdF0icgX8;{sNvx7iU0arE9vbNirIxMeND+36mMT z?HYRYOaILxDJGW_MdQRKsJhFHfe#yiD3@Vt{cy}Rw^x01+Mt!xPm9`T^-F`b?}F~O z0K~OT7POEWrN;pfO~yXgiRb*I*fHl7r1!&Uxf>zMZMAE;ZRWj|DkArngRO%IgVwED zed{u0YWJBA%GzE|BH82Dg>I79j*X!iUYZ|H=&-l#(AlXQ9DQYBfcR%Pms6v!Ra~&N zLHc;D<-9)}xw2wALIqRD)iH74>|ibj*5$#rwhr-s^FS%9UF0+5>uVM~iH26p-@eIr zGHNMhcatdL-h~c#yysWwA^e)xL0y7qfoy?z7AT3M1p%qiLx(BQS%|=rCy^*1hmwCY zP)*O|4S6hNBpCk?B&2hYe?wxfAGrc-yYAJGM-2}X5@f!{q^A=JA>Z1rDrPC`oET@} ziGatEV8w;#P}eX#grmuKX79s9q()*!6TXTq{H|eXLDP_QD*7v;lJaA-T>hfXXX@>d z7urRnIjXWM^2~8b^v0ZI_eT%UhC|nl?YQxyfU2Xr$x-W1D!3+E9r~8~Rn^s#N-Hq4 zsPA)Y2H}m9H*RpWYEoR^5lLs*EnT2#GBb!Gy(dxQLA#-*`&6FW0XH4ZT`Oz$2r@J| ziZ6CH>>PzvEnA!BmX+jv%~YP}LNarjxKf& z`LtAVOL?Pie;RvkI((!CzPMAG!aoi#S{50W%8*CDW?obkx~4Vz(TZ+Q7N5cuGuOsy zWat=&oj(`~fNqYNo6$OvExh(rFsPf3w6Z{iBG&l>C0mGBWL=(YZx*ktv~Ycv)d`Lc zU^OSaeHQdgU^UNE9YuJ5_BVUH8}y{`^uUwx3Cda&93iS!0|aKiE71d)3J_k%3w0yu zHMNA`z|6o2J7q|RjSzQuSGeE|7pAoE>x@t92iuA$Tzz?XDpG1(7-=8(GT*dj_;t8U zk*InZ&LZD$EGMAX^vG~DBYqP44%`#WM&x%W?`ZtN&F~Z<%RR3>M?Ga|hdd2-m0g=R zj`^QFSu62RiiB`UVxpK=3nF*y9&i`ikR#%9Rmo2>oiMyHY>D*laZU}E9FE_?oFMXN z?UV{TW;yeE5uLE!>0ZD;Lewy58lpH7$)(@TWV~Tt zm6eHSOM9L$ITAloJkoR_7H!SmNbS^pBIlv%=J^mQAij3ryUa5XJ_ydH5JjHFErV3) zz<}ld1g>NU5G6ZXGj{^ZPK>3i1-{Sg0Y*hwGQ^%5LEcFVvmJ80_IhLHff|8yr`<+e zfhZv0G$4gjkTi)GE{g6HUBlo??29rsq0hGK9MjUPW>{8>kv5J#GjoAi>!mfw4KPIS z2t7C0K8R?G-w?9FS{|-CW~FavYiWgkv+yC>9`YUd1YOv3+qKaLKAYm$S=9xk`@9|5 zV@~ttX@{O1Ms-751L~+lX3K}$spfFQov~z)kY%Q?OrIr2Z`}oXLA3j=XojjWJt4$} zj8FeM^V&$AiOW~fg zN~1UlrJ&c5T9AIM;!c^xgV28%0{co?6~3gk1`;d7#>@;v{z0h11`qjq=66Qk?~-_Z z3=Y@cQ9B2tR$t;J4+;6x9ctO)Y2;_?;A!L`CelUz_Yu@?57{EoYT3Bov186G3tc*` zy^7}Mg{6!Ni)(Z8t7;7F(D;zR97TQVO7=nV30v#|A}m!vpphc8h{=eVH3f_Y z_x!jo7{12ySz?n#?PE|K%bM`kGt6ypISlNUJ|IWoc=D6ilkUV=A`MvPRputzLFN^) zaUvdc+9rc%m0@YBKIYG49$hMJyCG8$*QzWmu1doF(Xjyb6l-F2C9rSM*>6$Xfw2yjiJ|#4zc-JoVAx~l|{fzaqE82 zUibfsh3PiFqG_bQ)=(iE=lz~!<@-uZLsKd13wGH0^Btr&38SSI{Z^l+kPyTzcPuG-e~W{FLm&@&Gbbd7Wz~H_qXS(<>!+4Zn1?9xVY;M^SG$x!Pv9lc17b z3q_gMeSmm!Q4pIWue*t z58UdC=p2w)n!50XH!r48Ogb#vb8N?6X^uKzcj&~IK;4qq8wK5kfm;pDQ^(O+XD79; za_*9Q@=;){L9bW^VQ~4xv3t!}t!PlW%e0sN%g4dyi;5X=fGOQm@;eQmrcmhj)7aHG zPz^K*6yMLl3+%qsXRodUAxat{e+fYs{zK~ zabE(c*cTp@>L(qP>a*NaX_V>!4pjxH*pEy3))!K!*ym{!ymemNv;+3EbG7vT{jc`2 zPdq3&PhI3*mY-6#1$M9#&ngnT;-B15vfbhRO`qxgiZ92oiCIt$*@TL2Yw$w5r`w_Z z&fAgw_wP(y;r&c{Q};>zE%%D-Rqc3aVJ~##+Tp2REBm+FeWJsmR1UB$0TH@VkGDiwvXVp0nrA4 zFldVxrgTyR5kp~yNqs8+)>9NFMU0-VbwrY4%vP`KNOwE%bR66fRQM(FD)}ZQU#M2` z)Wp~a;{yh17*KC#p}ya_IlKu zLr6*T&^}f;b@kDuB>Wq^st}ZsLCLjoa#I0W9h-{flkA9|NJ%5A=xK&Q28fpYNIPqS ztOzEIaHdRaH@GgJ7*`jjuBWk}mBF#2i1D(hEqycp+2!XYP4sUkp?8GE_-sH+;s@Go z7}e#&&aI$KztgJDSj}rtl!ZumwEM+&+@*wqfn<=7*gFB4evA-^`*1g6vQO270!omK zkQ1jI*CyzJA*K?D*n6u1>ciEvw6uIv(T<%Sx&wdz88koYPDG9;8ZvtV>hQ zF;@--O-`2_mmnLIFf4B0WBf6Xjy{lE&0fmQ<(TY(<7Rj!?is|by`5uyYN?m!&!L^W z%GRu{sX+;FaJUx&w&0h{-URT9LAO6JF)Zu3&NdRmeaq>dG(qs&@R6aRCZ>PZ ztw|~xGdyww=|Xac0i6?rw$4%W8ZE}&knNSg=6I>pp^5OeR zClSfo_w32S(FTppoa`-7$3(6fCi&ORpd2dhmpvRMl_f&uK$;1@8DiXckjUOh~62cm2UO z2n-tNuwTKxWvl4M@iv;70n%OR$kOM{!_nNlsG;K(a2;~b-|N4v_xRjia}#NmT4i$X zUy{v8>UO)K#L66ZN9lfVwpV?dd`frw{xRNlAM1TTzrXGqsXLf_>&fj z2P#4UG$N^isA3tAC{Qyo5&&4>q-J1Z0;ZjsTMZH;Ky&Tx9JOr7dI|#ssqfJJoMLP! zX+?qm!SXRtS%h|cwx!j={bBdRF_*#3^_{4xunx{tcYV0H+n4fVDK}=M4oDvhgh#9p&QXzt}Kml=Znv4W0GX(K{v|>r9?k3Ll_8v5OPP@wi5+mk< zverVKVpB~GFIiC=e@BM3^wFNQ;gS33!?H9ilWPl?ZX12sslRYH^qZjn6)7KvQ3>Wl zVg1eIRFD^zG6OLg8(kCMF_-HFjxzR48!F?Y2}W*&4{~))?>=8^Z2*Hl)h*AFLR}yO z`FX`@Ym7U(?_g);n;1zQQe`zUJhBVYs)U%BgerVuNV#a@X;Cu72Zu#Cy$2X_vBdOM z0b)^N@h?+PHJUa|5`0B{B*l*VnHvYndMpieO-3?{8b2qUdL0Vd5Z>`(j2Xgmqq2=( zlS6Sf++`}l*WES&;YC3b=-;Hc*uHrhM`=5^XG^BIx0UIiB6T+Ei&v=ncX8(0h{=C@ z&@b#WT6k_YAlnMg9cbRS>fDv`0&@-E@}cR~>Ew(^=YD$gUKTie4UFW$oO@Mt=XC{; zH385r3pR(v4%yTHj#!Hcer={D$_|JufNl&X4Z1d&U$?01wpi&gv1JuBF3~%E22yb+8 z7Qul29Mg)ns%RaZR)_SKlbdX^TkI>;on(a7IA_25xn9!ro^GP*kRuYW1T&nh{@DO% za3DL5gg2g{qxwyfO6l)FK+0m>{87T>SgT(E4^(ejy+w5gftw4*hM5p7H1I49V`;I9 zkXb(xxbaWb*ne^c<(ymsx;20Z6r+me*3WpxcgsbVkuscg!F0T@lx&vnXYPZ`;JWRB z6p5Vs#(dsSr|#vx!(y{+q^kPj^5XM)G5GVm0>lnsUK*S)nqEiPev?+m;>Gs8m3Sj? zO{l;GC1E@NuMP{C5;cRFpc499UV?dMMDEklD1UMl(yRj659`&soZ>^ zHsjk(JBBUKB;8E_bVuD!D@N=-u!9}HjZ;GrTQ{52ur~X3^?K9k0ygx?4AyL9jAEYG zh#~{!{;a*^>x70V!|Re;T0(Kqry*uOM-B^f!5O{dr;Z20Oy|q~DimtLJi3 zWRu{^^nWH4LjjTE8PbqPXXg%gdyqYdB5;$v4lT;pulR>#XFaJ+k(Ty)1}NN#yQzuL zrZnc!;r0bsoO=xcRmoBRQidANp(~=u(nxL~PBaRYfJ==_c!TNDp|UF=Vjn#V+l#54 zK68TRH*HQ}hfXdQFfSh<2x5FFS`}FnjH#pO(_ca)^7U^Cu=D`2<@e6%MO{U2Md8|{ zYd7ZBV^iEU@R;pL~DI#Tk9Oq{n*9+Yp-|AN%IRy~7gNSv77T`}n z0~9P65mI6>IT!A_V0+zsK$nrM8rdO=r3_#7W*@G)Ht>GlwqNy_@lKgaOGGsjr>_9x zz5g*e5^Hnr<|wk1rX7ok+bMEBnjVTCp`OymWI1BG&|Tlv?xAT_X^1fJL;3o!+wMLf zw`Tp#i?UnD7-ox={&+RKUblxUt6@E&Mg1Y{m3l%=zGbtQ9&F1Tc|oSuB4E>I3G6-% z5-EF<0(4RZyfXk5&ONpIr8HNWGrzR;N!MLNFi!cdGD-o!T%pOoDApJh#*4b<%#cF* z7r>L6or2mEzt@LiygHY* zFUP}!@(_!XE{ z>D8-~7fp^nvwR9!8pNF00u@48CM2oZ8C9^ScdRXpW)1Bd=Uc^xY_z;Cr$|8f$RAES z4C7ix4eTgL{VN4QO&mKxKZyN^J23n}`?rCU4DXUqjlg!J^}r?;5>V%i^ZUQO1YEM7 zYc)A8k2!nVi&|fV0Z*rNxjp7TE!z5oUOxSLckFq-e_kG)`BrT|=ctf9TZgHiFI)0{#!J$stY!EQOLzmAA2s0DnS;wHC%Uh#k3; zLsy@FciP){vz@tNpd#z+^w-5&=ht;Q#>BW|$efqC;Vq7dm}pNu9T+S`I{j`S9s9z* zE0JeAH-UKlD0UHfvL&je@`trY_D3*RW4t=50YXD8l)0=FF#i|h=eT*w(`*Ha=D6%h zjx;O9haCjdab@#>U-ABV0~v{)fqq)26eTza9d|Tmj=I!f352$u4u9^)v!*Jf9xa1; zRr3K9ukREoT6B^N7b?7F2LiF1!TaaP`8zI@Iwe}LQ3xwJKtU7G?{)srE91`^?UFyZJ$G*@|0_`pJo+7I1+Iu?w5 zKE0RPOh)t!rNrJvTU<{1cHQqipjQMIXO?&~wjJ5-5Leo}cpd+&=E`|_zMfjO9MyBr zT?UXZmtn|XvqKY$Hba{*h=VQJqS~N}D7Q#}TE)*!M315r$4#(8vx51SxM<~1=LqL+ zO5!}`>>A`jST8~`ti*@V+Rj-^CQbyT!{vrqd&;?;abeeY2mYzX^%O=`<9$88xlz6{ zlGr&ky~vB3DU@T0tMRxHyi%D2582ry+6n^0G+BodjN6||O;0v02DQV#vmV#$*ROK+ zdMqU~A|dEQTikhIj}Q|iTliz#qv@NVLs+kwnK3$K_P_`XY7H{*@kVlzOudG0 z%3}JCJ64jT5kFk)-ffU#i^Rn(kkwRUtDoA#X541n1{BJo{9=~%;*dK{KA*g4QZ4G? zxExhO1r0-Cn_c%e=|9)Wc$wbSDXAJHj?1JEgC&nfwt{~4%cK(X7+=>QTw2pGo1dNrdYJgTQiUm-xb`Y91uARyM0I)5e z#v*M{c=wQF^;937081)|s@38|>J7lO$ilKn;78?CgC?5=nL;e|M|NgCM2^OpzjGh} z(g65Q$X%2ZhW;IV_1lY;^~F;moz?0Mju-U;_8024&W}&v9+=XzHHTN^F6~!32K0U1 zk(xon>r^`vI`$v>zmS`TOY42q+QGVgwa}*4s4iF&oo4rE%-u@m#dcX_LLK5lKxmqo zDKs#DW7!_wyiU z$}narNHIg<45lPkU|kR|pg~p4g;&2Xm)C*}NM;^!iq5mp^G6<&8IcK;P9z#E@<~yz z*~SYzyJX|>B{Q|RIA3$l@hw`9q4$t~5;fGh5DBr0Y z_Z9?-!lT~w`0FEhl)FW0A2(&I$FO0nJ=5MLH(n)9O2wd2p@5ej9>;C#kJ~#0*9#-Z zoj_$-w|g^}T%dOQWO|X+DW@>Y;^k5EBh=ljrwAvR@+>N9gv{dSF%|vb70N2PYE;f7 zBwR@mrpNq49479~B)HD;lpUl8vL`a?8a;+W;=B{(t0jS?q$mP&y}r2Q4O+%C^Kfz* zD#Weo`igQYC&NKb0vpgnFn=l`$9+=%!Li`GoyGh(oni%t^EIibK(S^`!0k^l`8P3k zW%&Hi-Ub}pQD`XnZ~Pyp@wFn;^-&6->x8-)Fo8{#GMelbJiRel@l@vsz>h66>~233 zNc5!7ih5(dp{WFLyMIQv=y<*sUOcrKqHlAceZmqGb);W+iKfjiZyAW(ePBUA!OI5( zJmXYID2Z*m+#TX!j`H3BMBu!T27)K&(eXg(zG&Bkd$VNZHySdj(OL9r`MzD?;}3o1 z>ZI;(5ACO3L9vo;#i-iVS=Pl~l{{$=!mmRMO3v^uB*u?RKcNv347(v?GfLyWhbtn<4s zdFGZvhW8-q^kbx%%bY}m+ZXN)M-zLUJ+--Iu z_8r9~P`}d@7=Ds+d$ySf!Qs%z?AEbyLrmIh^>m zC!tiIWOl?&N?$e(%x|XnyJs$zQt^vGG0kl5w!?BNixBWg2fv-?eK6%;C5sCln z<%>TV%m1$&U#35~++SJSG6Y{a%KU${i*5c09RI6)tRO1zzhA`2PH|OITty3=?qchj zK_Vm|xx?~+B$(q8Lj{o!7x$9`3JD^05dsxKL8MSIQtAa14J9-bMu83tFj$7XL695n zlNS;`+>Ek>ysTom-O6|$QQuxYZdqP6z4Tsg2atOU0O@y91N39gS3|k@-KT|wJU;jc zgnu1!lPTeC%>bUgj8&Ak4NDKx5CA`S` z-G>zY5GYBpVF;7)6J+cznSwEdfe|eqvfu~d{$P#OL+KG!$IrA>(t~^~^(o9w=A5Vw zxvdP6-NGo4e62K+9n(TDaN{l|@eF{&NAmgkrz|M|dX#kCiDS}yt8hkP6wHh88z2CP zq(P2O2e`;*4znBpw-VEX2#|1bgA&B$H1orgQInVJ2E}Is2CAe1>D@3lsp-2$m>7C= zzZ%;VVL%df>vXgD^D3P4bM>Vov*(`GZ9E^vckWS-SLJKcv*{fEs2ufetFs?;U1$Ig z#2T5n4c*Dg0FBmQe22R~WG@K-Za@JVx3X^WLacNW2KQjdpd-)DtQ-MzEorGv*j{r3 zY{z;+I3Cl-FJ4u+O3-=ziNsNvjD-(NN=qb#CMICuQm2jH9}zzHdpXP}e-2P*<@Taq z2cIa*pCmyBg`0H*fA?35qcwsG@?G5Z#b6}^$MngzL@{&L1@I2$kZTnOAGPHM5F`R7 zw&C>v^8T%fzyLMc-Dy@8U*j0d=<#v<6z4Br9TOG|ixjUaX#GZE79xivAfF4%|pD<{plnPb-ENqYXS9*-JH>HGWfD=uLx|B8h$+$={guQm2(?MMNxQx! z{^T?gb-+;P(~+$v&Lz+#fluI-OCl#lk(Iy^7Fd>z*qJLqn zO1{W$V?TGWxNt^6vO$HX<-)*5r2&rXWuXG$ANuVlhxlVrf;*a|6S+eMVc>1%s9+AW*%uY z(|~RQ(KysNZqlJoPQRE?G9hV*rUqtmUCzs%>6&#lTW>JjbH^d0M+Az0Ik0o;d*(A0GeqxQkcJjv?j#Y?xd)u$+?V5bV9VbHJ87icgXX6<)daU9}ljcB8E-R$xgl-HB@ zHGaQLNH0epy9`$1{vrB z?CS`?r<>Cy)>=RV5_l7I3H}br0;7%k;)p>@&HRJ(%)M3F&Bx6ZgvLNvNN})2EOvBm zR5UUgO%l!ZtMKxW@S>=+u%~dUuw<$lwR*#WNR&ntK?ye+J~pZ+v5Q0bFLr z)cyr6lU7Q@*RG}MuzY;FI!?Dw-JTK;8y|NY;WHe$zx+_D`cAja^$=t^u_N>~;dq$p zIQm-q$@ncEBsHi$XjUk7P&?dEyjmPpd>=@bziG$0UiZfTpgbuqsgQ!He71bjJgD)h zabAE#KcbdOn~9sOi*A6qkY2ET%zW`e8M%OOHeQO{QQf2?E#^b&UMz|@lO#|by1}A; z%T&}jcT9c)@#yX##2k~v-bw$dl7rGBpV68x7EE>pEoBSg3h~&ABtaHcU&M`H#Se(Z`INb)OA*8s&}-j z4kvC3Zdy82&$?CgHSP7_&0~#X8yU;$yCiBXL>Ehc75(zZip@Cc`sLYjp1n4*zTC5I z?K=+`4Lk(SgNK6C={x!;XIaH>J*2;}`(t4Gcfqgr9#dakU!ilc>-cHWVA0}{{#fTl zu7zc`GPWev{^*3^x_$c*+xMM&K~Ln$Q#q!>wkHtwZyeWqBTx93IWXT>-6Om!PRZVL zADU-0A~f=sHkQJhi&)J)r98J!{pPcAr*LU_Ge5oVq8`#S8pcc|r{kwkvobkyU43o@ zhJ&vo!_h-IRh@X+8xQ8Pt81&b#{E-kbgtVR%GbMI-;`4;O{+PyxxBNzyUs)2qnGeH za#D29e9TXqF0H#x?Hb-w_N!vLLA{S(zP7$w5AEKEF`wDkz>#1t7qs1;U$^(UPrbL^ zXTi(ib$BN{*x$|G4bS!q$e?5?b1HbN-WQ*iP0G9O&hnP^rW?XcV?$a6={}R6Yh(vN zr^~0QL>fhYiB^eDM?FVvdeq$wU5!Vj&hH2JP<%8$CeK#p`nbM!nHu~Yyt17Aoz5$< zRo2b%vh-kmY;*@vhcH>5BZF02z z7VyH%0QL_yjt7;fbJhAv( z0{pAGB{kZ%3nyl9jNx2RorIS3Lm~j^`O)p&b!=nhjS%at6S41 zuI~%_6#Fvg1>}Y=e^;L1?X-0)s;gUtvIVvU>N#sy3N8I=;mqKPgHbnoPx3o$l!$%V z*%Yav2;J{e<*9MS@Fc!$RMC7#f2U8vyCPjT!(QbmXmh+lBYgshDAuT_bn?It+>f`h zsrIyGL!RlqlV zgN8GDY*XZ-TDFzUGYH8(&OSI$CQcFwTGq~?#U>#3W=iQc;PH*ZdAZ`KMxTzVc6DfwepF8z>hM7Z2qfb%4I#d0J^RlPk@oS9 z@=VON?aowu6nvE1lta#Sw>LYJPXqzos=CY6}%8Yp)pdTi^b-vYq$F4th9?2b23sCh8Vag(LQ`MQ{Yofe9UWe|BPw;#$~sJmaO2F0AQKC} zjFcT5*<%7~$?1I3c_OVX0lxo8M5gED+_9#XA{k}+60EgX5x#?R%AA?h_5s_5D-f@A zSz;YSRb8#AsG)%L8f#_LUK9;Qon%IxJ|t&#ZSCrhEF|s@B#b%X-+Jv9cG-lnzR(m3 z{p{!ZtzxEGhU|TgaNmQq7IG!yb;MK<34Y(+wRc=7b^sQy$XUDnihC-#x9QX}>*n{U zJWVFVh0lidSp#0=273BG zJ7EXMuPQ&xjDJP4SlE~e{^#R=-6yX}C(1XU6F>-l;sy)K@y$HTo}JG$EP3NMrG+5$ zb*ni}qO)eRnA9C@WG-jFPPi^@#HLCI+8bacCC=w(alJ>ST5={eV#+a+meV;V+}_BH z)+`0xkCEo;4oM+Vn#RkyszkdkcM@M|QEHhfwH_Gk5sLK>jPDIFZ3O_8l7Ht|!W%a+ ztO1<`SPnQHBeeD;2Y9ITBOz{l#w+&QGzZx;2XSBp;(#^|)WRP0lO%z;u@2g_2650- zHg?y7O9w`n={bZ-;3K&*(9FRMvi_FFSWY*r?+rz2P>saA^oBr>>Ii8;n~#(s#q3a$ zErQo4m_DTm^;4Pv&a~b_=+-LeM`gjpWVM75Vwrr@Oio3jg~h!=MmOu&#Ftpu5KdQY zU`1tYC7i#LgsU*8^1L)B%*Lyh(gP2AgDbiK5qD7(daQ^X&m{k|7=mk#1>NL>7096y zSEfUcU8E(qVqIwpi3X$*(r|YmywrHLwIKvYY z!~=|t_91yri`PN9yndj_!@m9SHAXzE16|pJHAuw9;6@Fbfm#0z*8Vw$YkW^Du~t_} z*~?1Wg+l>31(65cVr<tcEGyjNRGeM%X$q_w{qr0za`2M&$7gs`LH|L*2aK|mqJ=*(5%(ug* zcd<9^qcwIq5l`>RH3PCD6x|=dvzXht|K)vUVEQlFG>r7jjQ?NX$83!8ge=x89^F2~ z3Pv>~2sxXWE|;vq@N6p?dpH;~ld?kwFw%#Y-}6-|7<%D}W8%`T^YG=mNVhV0w>p>I z9oyWVL&+oRWodg~3y%GWV21I|sB8Ku#>?)zk1JiT&$rF@&6++PvC~NUmGPuk+g$Jj zz|+ZTS0T68@$rt_0NzXAl{z`bWOo7zV@TR~l(FCZr=+{f%N-rBnN|ULfW)> zjm{R|R8Jem5@~Eg!~L|QrveF8q>fLx6~==&k8953=PSTOnWZ+efbSA0`MJi++5L?6 zYk&B&Q*{KBzp#xuT!zY1Wp($-oB_T$?sN*wLsnF^Nvw|Fp(h+a;mSC?e*9H#=d`^JN`%N^rA}zfvoXx?SAo)Jd@AQK=BYVkoo=yzBy9pCwBX z;0clx_s)O}A;QlK#Tg(*#MKNr3}b*CIu#I_poD;Qn};&kUfb>VIG+T10R|8K^xZ;2 zCcgj9TTgG077$Wef73iT^gT&{T?W|)iM6ZP8PA#V0lXfOVo{I79Fw-ajE9~SN|KfG z`h?1rmUyoctDr5UjtRUim2#rs(7fGFb>k%T5N(Lm08uzRk6_{U1I6w%6Gw1M*amP9 zSq)+rj2a%nuRQGi5-)u2q!Sl{IfP1Tusp>J`ONU!Pt={(wt7UrM8ouLrBbF0LexX@ zyz0Q8vVOgmg8oqQcgcmv53-Hd8iv%di`^$4(i1z}LgN5De7u0@Nb!6K>i(vj6;{UX zNkh#LegK9qKI1mnd6xr`pQv z$jdE%Bw&rZyH-FEA&8ZBcj)9n>Bq~Zk*tTublIZ8Kf!|5*9^sgr`1cX;2ioeF~z0bn)EzGKValgQM7CU>ilAaiXIB7h9a3BAtU`^0G5D#SjY zz8|ad>Bfq4A!w_{J^c(={I3Tnkl0h=7twUQD;-L!YMr~0PZGplhuvDCL>~-7TrGN z1aKDJjgN~3Vf5t1ZU%nH?3n|Dfo(58xG5X)>Q6GAC7LH5h0#-W!3IGk>BXdkhOa~z z`^Z+|4UO`pr^TP?7KDs5VbI}Vy z=oj2Q+m9B+&w5HAde?r(E?2P02ly(80O576See`=p=_`Mi6Nya^{vOtq;||EWHNZ3 zRR#=82off*G0M!gRu73~jUdLJ*z{>HtIvE=RDG@&VQ!L2*KwHB_JbUapFZplPu0L> zWDRaB4&EC3mtdnViFpEswM_y5ww*6XagX~cYa=`P?QO1nwtzYDWl969i@L%_J}cpG z;KImRtNuD4vnhaLv`dyjG$Pjyt{-~Xd2|cm-*?)|s#HWX@xuC%9XeYjWu=^9^#Q;2 zj;bZ@*j{Ta8yx3+>q!XfSF5KXXLKoC)K4sta?!kK76KQ?>@uu9hTV_K5mqxS`cglH z$?wVukOG#?_3gc>H=2z}MuLb12~MS;l1fQfN`;`=na`gllQdc_BOYZ|TX#;`k_AxS zR3iKLQ@lKI{Unt(MNy*=ZQ7%Vly80g%#zw-->rnBVG^lba9-;JGVcIBWcEBQ`gX>MjDT-v+t@=2 z2YEX4k#c=dJ}V@=Jlcku#T`4H3OOqtAeH2#bsmC3@wSmbGQ$Gf`CSIzgdcJBdFX#S z0Kp~@rtyOW##aVqKO1`-^RF|reU)!;_M_t83Y3ab1uQE{k`l{!2=}og8d|8%jVMiv zIi(GOfR&b5hXlSoZ-D&G-p~Y5U+`iOikK=8nJ;XsTZr{Kr?&?Bk+L+UOW9xMg8)SqsgyZq2C+Q(Uu3>3noIIw+knw2od03Wh_qac= zFK~ouMb3X+OE7=!v;WWG(EqG`)xH+L#)eK%bc)UfPXDZiMQk0u*1~_j{?md1icU^R zSdI28QP)^T-`bdfoR-eS+|=2@*pbfC*3#0}k;dBC+Sb9H#_>-zOqemYCp(ydiGi7bgNaESicZ1U(bn0) z(AbgSk5;XmgRP;Wu@iwN0iB$X2mzgvv6~YCow)Va_yYfa7yS3TIKfx%|Esv?pYDQ= zU#hkYUrVNc&cFm68UI*+{t=<@RkZVKre8HZzkmN4Rq3B*e`2KYW&G2M<&W`I*^}amtBm|*vi*acky3 zL;a(*{B=GjZq4+^`j=GP`p-cBC=>tV`={eScz;@c#p=uX58xm3Ys4?}KS=+Wf04c> z|KW#8UH-!Yl{Cw@9RMMQ(T=3gk*f4qO882&JF{Du1ZHT*LFLNWc*_b(K~pSW`Th59pjj=xO*jQf}A z>xKVv{$=`lLl_w8|1vRty<&{?f0-D*9>Ylgmx=K!GK}>94|{I`71y%ojpDAs9fAc3 z!8O6%-CctQhY;M|9fE7)?i$=7XmAS>Ji+a4a%Ao|=ggaP@BP-S_tuOW`#>H+#Vzo`k!&&%Iw0!$aU`#Vj{KQDhL3Q%(3?(b9qJZ5J6ohV=)oQWAQ z-e2{P8E73dpd7zm0*qm1`kf}gJb=qzXyOH4zmEoRay%U%{NHU{F#1uV=B1cj|K&Z6 z8_mm=^&d-ZJ-*PwVD<0W5rc4gpy}7%7(t;duC8==$?BLjxgMlK)rc`@3-2E zPwaY$o=b_tOyN?GXYG$&ax19IQ4hvjiRLx2n~dk5ne_tKac`VR7{1#P<($m(y=>J9 zv|GGA#bx-$UH(xiqxz&VYV5#1Sj|5ME$olwK`hM`UAvCqFDLKKB`iHtgA%{SPzD*mrK*X3-<>mPlHQCZ=hn`| zN-u0e5FIDOFj`eq?z-k~c#Xm)VJ*P+5jt*zOQ6OW-dr%*x#;Eni2m58#)9Z{mRfpU z$Bf%U^;eswz`YuDYO&}3m_M;foWaUR+K!c4v30P3gG5$$ zXzz1qM|(z4Ykm-H zC)Jfv9p-FRd4*qEiB#AZ;hxnZ*#+3!izdG8yC}wio^P38>o>7!Bf>;v+OB=q1RJbL zu-r+MXBTbrzW(H~hdJsVZ3JOS50*Q*CC`qnX}(w8cb?VTFhmZs7n@KRHVlq(!RfeK zHxb@sUI+nd0IPRzvfFW!Ke|)*xUD9=VBDEj?8R~rCTxX^25bPE5P&|p8uK;RYn4RJ zn6}Hsd(lLz*JT4G{*IG@o>y%a3(T{ay$3126#k%iWG<#kt3RZUnI@toCI>H-b}b)= z9Nzg@UDtVUQiQlmBfMr8nWHKxBP)-trg8vyCO{tYGM?H%VjO@7s{l0%i5S-rEmQ zWUwX*WR`(HUQelgk93`Tr)*TE-O|Oh*vjc8aU`s66g}`v8oe}FSQ%clB`@V3B-%EP z-^~+`y3ruH0^_y&WikqboSMA6YVpMlMd8_SP2sENp#0uyMX?u-bz)JuPS{f0 zAn1ye*skOTx`oTSoJDVLZ$5B_#;&Rb5$8^oIlA{d22G*P1yHiDyNocO_*3(PN2G>w zklsv9AG?D{((B;U42Cn{xh==oqw3QGR0uv9hRqm!p~<2Y>_ZA=)h0!fRwq~X;REgi=%lw4jhP>D-uqgNO{G(+QV|ZuD93t4T&>X{eo>8r zXen0!SlP-91z$oc!Qvcd=zLZF7=3j^e6C=c$MWjEw(5LMHi^d83^x%g&WrmkChaUW z8Hvl{0^zvka%r>-QSq5TSC(QQeTh|5(W!6=>{xMmi`?U5+6sDhkK%-Cq#B`Q_2%AS@}ENs`FT#> z=GB*%>*>F(AU&vOQie+$w6>oswuR+o&6b@wfEb}7Ggxidf`1k%o)~86wC9X@Lfr02 zh%4elvdtagXDlOP7aa5ATc+5vy*B|QkDvjs#7js0M3|m0#!(@yx;Uw?kd*8w`T2L{ z(qB{4u?&W6_;nEl_7pRIRPaR?KC1=8fe9lw+s|uGp`A3YC9F(ms!GgEK{Z6fx$7Z% zE?jAZNfZoIT>7pHJ@uPDqr2izckZm1X5 za2&uV;pw)Ub^g#Pg=_0Oa+`Fb#rvFzpJ9-5*6@?k zRT&bm3LM{%4Q-uXn{<6Tqr>xAi1@pW+?Erw7$egexlj~gzbV4@7*^)UJT8cj>qmK* zL>+k0HA{z;q#K1N7;mc&#C##C&2Zs6F=lNelF2Y0UO<`(o}GvZT|pmaWK75}sO=|z z&bzshtt99snVvO3SXX;$ZbWXk_!WF3`a`*`ey!$5WsD0|>+V+JGZW=f*^_0;nVO18 z`}1Y2HB)!oCh8KeOV(GeglK6}<;z!3P`l~vytHgcW@#B33=ERk!5F~)uvh^3|g9GGm#O# zw`Lj)1t*fJY{M$#L<@i>&NNQW85iiLnKvM@f#G zRo2~1$A_@DX*{=qj~!hrW&Q-@@Wl-y+lvj3rCzr@_R4y9yTcF2^<|G6B;Wf{y)G!t zOB(nNz3%EQHzQ2olSo_3ebMKB#37h@&g4MFZdeK|3(~7v1kJI1s=roNB}vBYE1P4h zVX<@5fsWa)cFjdYde=t~VAL4{gcyBx^Y5Q7w+0K;utK+U)OF585oy7_)6dpNsk;mnDoCB?J_-cR=4pew`uAd@$aQk&q-r{KAhaE0S6%)#)7fYvWsD>Z(wTw zhVrHpVpbU11#P(+sFcJ!E6JNIocoYIS|^d}i*X z3Bftch*xc$*rPlgjd<$A=0})dO72u1?g9vnF zE$tC-sLXm=kg*sYatsr~{w^ z5Y|*x{FJeif2zQhV>1X)>u}#7qR~qCi7m9n>IGKIlC`EmG6(UappW5Vao@k!vOeC0 zj=qmhV8;r_dJgAnr-Z7?T7Ot*>VmEo0>P6}g75-s#D?Ej`lzRLjV!hz^7+00(XnyP zYb<(i`UIthcUo|i7&$p<*2}R(FC?`NoHUi7#fRzjhbyE*e94`6nrVBeUbIPsq9mY1 zXQ`^i66_PCmtxoL4_dEUs~*coA*6oI!~3ygzUHTn(VtIrf%@)+b7p<^TaC4V={7v* zMfoK-74ZRRU#@vHyE}n;%$jI>VoWY(L@Uy7A(nU=`eG+~N~+>;lgLU3(Ad4;h*ly% zoJts=#2>1lu!mnEM?7HTp5nnA7!`pW8x`>xjHInOj~>C!KR~|TDtOd1euq}2aTfYY ze2uJaY0$J|1Hpd7SToS%aABnF658vOQViA>`tc3ckI~gfZm;&c(>XooBbo=T4NtAe zjYH2#BAnD|o}gM)*!q>DrYk(-w(Lu#=UWGr|P6XQD0OXQCNZm zqHv8`-sWr9VLB=`Jn{>q!`Fgj^A09g2Uh~?&RRD zGQ`fkjeMo3CXG@;kN9+bUfW-6fxY)vr;i=pT+?FRe7ifny*nkzUl;y}iZ5&W*(9t{ zooCRsAteC`Cu<=iWt8UQAYTAtC7R~vY<|X8#07)TC*3dARYEJz&29L-uQ=A>aC3yv z2*Ke?7|rn}RRt8%YsJO60kzNzKHfs1fPhbVsSz4TjS^1pj*iSo5a8c(IG89dpG{XE zW_bS1nSO+_QRt0@Yx5jx4I?Rb!8*d~b~(natM;bN{AION<=`t5vbf=Jo(2c27XmM9 z4?5QF{v^o2T*i(2!1tTgam(tazMg$^j9rhJK_*;iH!C9l8~OG>Lb_I_>N zx_4979!6LRsRh{7T2wuOCRI8kzBgvrOxLt@>avVK-e_e@fCG!>29bWK*Zi_7U7IkC z{KE1SbgYxrj=RPx#(_3W^sH8=r^PCA1()-ht^wo^ZLUJosgyvN zjggcs8Ms?HX!nDL#wJDZyC~uo8iO>}0Q%Avx7|l4E_nRt(ZRT8f!7x6CF^Gi!IA`R z&c+COC`!fH1m6dm&I;3ZC1R~+&HRT6q}te}1=Ch8BgM&*$So)(q1j){hh<7xvICn& z8_R6p2F)zi0_!_s^Z4&g+I7z>Z ziu2Q@P>$o#@ zBLX6S#evdwZbKKEdoi#f{e>0!JGs%d#CEcxT1!I5QE=VkF(JJi=4|vV#_l$XsVL7ssjS%>| z0s(AI@i_%yJ8msC9q;s4=M3w+-)?97o&|bW!$L?Xm}Qhj0!~Pwpe6xsWP5}SSBn~v zW3ejdWe7}igrMMR%bNvXe--h1xzL%pmZ}Lw7r?N*?DaRUMQF=xu;6yxv1q5t9XBtl zoO%cOrryTrsgZj3P!#d&kNKS`81EoJ!7h-aF?Qm>L*GCUc!`jrSV2KZ(7b^J6z=+r z@mbcan(1JikA`uLpNznmx0Pf9mqyR58Z-oi!%I5Jg^Dy@tt>AdI62k&<`O1{Ve|1C z4#Din3hol0~>lW+6Mnx}@Iy6!}Xs8BY6 zJA-7AQ3&Ja*8+5U5*?In}G<0#83@>Ge7tj+VmO`5s<^fU*d zaMd+6;uVL1bPW@(Q`}eCG;Cj#u1=F(l3{IH^48fR->=CLGk+Hc!|j3#!UyKmho7H7 z+Ua7a1!7QmyK3(u=y`m@xfs{pv1UU=GOD@opMy|c7xP)O4dF&eP z?BCFiu);S;Q;bTxKHq1jlUniGI+At!As6N0o+xU^g6@?;1cL%*>Eas29C5n*TlcLgJoh(fZzrIE>wAboYF#Eja^9~A|Z#QsG z?$nRCM_4UDhlhg7&I!y~oXM_Y?xiIS562e89O+UgiO@nA-klMsvO-5+*!iG1{ z=Y1&MdazrB?GjU3H#*OOE1qed$qOu(w=RS(W(j5XP!eWj$zz|+pu@;1x_kymg$mc` z!*$awu-4VJDJgOzai_Y$SPx;^+^ZHGYt*qizAVSoHjI*WJ%O{p+%`t zLcUAGAHi>Novs~8eo3e5zg;e)O>FEhs}*8yK9I{ealAmLC`H2AlrO(Wkw#6e>i3%z zh~bPi3gDWVnKco|P!_t>hx*Rm{)JiG@W^XeZP`g#{GI)7{HT**zKXXXslC^1+yXsVEV4DUUdno5V-3R&IG2NvedQOCKZrSKzN}|q*Vm!WdHc3S)0ip3WqkL_radVaV14dAHXN;ou{7M2^DT1KD z0vo0Cs;lpYeeVcjP-S$?=6fO#F(DmJBxQtIh7X39;fm{+ppJ7|_V7*f^z<`eRM5lM|* zs7wH~-?Z<+N181NENlDrU2Gu~oz(?RMD`|wZL9X@8c`aLp}p36as72!j&-mx>dz!S zdEE)mS+L8Id2CeQc9M%aCZ|Kla~>MnUoRKqFR^GnCL6(gjnv6~W4+f@B@BwV3a+)As3@li05|BCrR{MT|<&o{=WL zAabQ&>VctkdFJ~_+<)%3p)A-CdqZgbrP#)bDiztM6^R^n&EA4r?LMK zrVMeI{P|ize?9CJ5_o&<)?L?5v4pzku#ya|(q-X$eIOD4B%P@JlZfBwfB*C?{12+LS1`m0<0B z8&BgbTm=nZl2tT2548KbrHT=H^2C;`I&f!`h#=kMnpF{ve${&~$#)CWov&Cx+rvgV z!FLTBJJ@$EtI^juCL)awgPO)z;I2W7geI8RTm%W4iIILlBDkm*OZX)vzsV_j7A6$x z{7Z3cvU^b<3NwCI%EJwUc87YatkVIzYYOEzZ$DTeOg-oA_O(?58)OM0xy`8gy!Dle z;Rk7I&-Y?d&z%?OFW{tnG@@zqJ`lMT_-ujP=^;Z%)gebDDScz)pcSAGaML&oNJY>) z#<6+`kmV6JtSBM&`VbwVC6@earqPfY@e_QnE{zd^kzF8PTmbS#Mrd>r$GX6s8cZlk zvcPtQ=wr&^UL}8KwS3Ybjf!V%>95qH>8pvZytYJi1b(DJ)6oa>(#U;8crO2)8d1?- z-CkQ@S`lKP@d{2JpXZyK9Cxe7uFf|g{We-1?eQ=Y zCD$w~GN{>?Q`}Sy!Ty;bYpHi#yf-EkFGRd=bhQ)o;B!gXA>WpEyr+)%>O6|%4C{oe zu^A1#NrjEu0fIdX)K|ma{E=G+T+RoI%%#D(-DSN2Z*pQ2w?^CZUQ|8Z_H7DdwRc?IbIk;0-d8U;D+#iysW$Krs{62Yqe1CRY zZ3H452MwBEnw<#`754LnS}tHbm+ds#Xwu>d572X4j-KwMQ4pe#vOrsqBcz$94wr`g2Shb=77w)L5Z3;fHi=VD*!9Zw64HjDf zKKUX6no19ZG>~fTj+4Ew$wDk1$i6WKE{J&aJh%0VI7rLD{G0--cuBP?bsGG5U%YC9 zFipDv%plItDZo#{aRiHQxTUO*iy1)0JPCmRwUIE&^^V6RWbw zQfX*jnwT>U1GlytR|X0nWZ7l2S3O~&YcuIonHuQtk>p-z2;apbH5mYmkaiFdDV5%W zx41wnK@!Koe}S8fHM#MHtK6tE^X745;aVL}p?A*VJU0E3g#?<_QY!6XVrnJa4^7~7 zma4(z4a1~E``9W)llZj}p~@sSRE2XpP`WC#@z6zqMPt`^wKQWeETx6QkMC<}f{62j zAjIH777ZzZ%J%h^lzpEX=Fk;`;7pzKYwaoLQKy=4Lea^` zrJl50FCjWLU9Gi5usN77!{D#g&%=()5G|hi8a)EC#m8^HtBKeOGo3d-BkqTN7K9&je9~rSJv2|q`-#oT zNoqCRwa!OVOedq^q!!*%bYq}?4e@%#q5hW8Www_?g~L_8rrEBdDzotvsqI!6_SR3X z%s}zM(ekYgNYU%Z*2%qTuhwg6S=Gp0b`)$@PC|;+ce5IH#MGZmC_huBH?ahiG;{A9 zhVj??%qlNS8!oLeLbk8F*O!c7a+(%*Rb@5vjTDSoQyW)NXd%!xmFD4KAE4S-&W)VC z^?Gk=Dw1BiEm=Btftw_I`Z9BkYT0#a;>F7~Ck-&!^tw(}0^ZI66$r^a?^;=zy8Hok zou+rUNON^%rpqve{Z!M4AE=+ZorYSyv0-;;lqKM6#Vp#H+m!YT=aC>iEsEh9<5TH2 z+<4Z9KXId#`O4r*D=$_3d;Xa8^Ay_anNss}G5C4GIuAXR=o+PihfByv3|M0yG5>>7 z0#5jVhOCH}R1yyP4u+iZu6r^x*cM~YCIp4bY|nUo<3Jxa(5ixT^WxF9b8ZkteJ@X@ z-w4@4M0VKPt{w07#9zP=qlb$Vx>t=tjs~KvO$--xX_Rt$U<6wU^Yl(i=kRvjer@}t zzH!QOmo?mJkOZwz1wH3N-1%DP-PEP4`nz3*Drz6XpbBD7j}y7P-FgbSal1QBt$|o| z5$Yv~3HSZ?nIEE|X=O`JK)E4Fm^10@F`%IEQX9qDPP{{5f3FV&i8&naPbabD>h~nn zEnXR{iSxdvUeh{yeX;Z&Y52-^=al@2pnW>OzgY;6@8;FM=QiRc=HhL%$<+IgDr1~t z1QZ$HHk33Gl^W6s~o;?_BGM+Sm=8J?A8b79Q<|haj zy0aCP`h5%4{Mv$0*l_Z__5ND!dee;WE*5&f`dsgap0iat`?2jTTx*3?(zz&t(P`~w ztLUd|K2}*dW8uK{M|jJ$rF*(@nQ(4bOf3QzJT$IP+;9ox{dnmoHR3+gwMnnBzNW?2 zwKi$`UCQt%KAv=Fj^}Uv8A|UbuIx9I-Y>^x07w)7=JFG!@?XLBexl(JUa6TI|2)+l z0Njb2n47(IBn8k)06xqH*z>{moz>H>503UuCjSRU4k-Lrtc{Y4frI5!Wxs-L1kD{C zy&tYnk@hLT z!2eX*PspV|O8ZxM(_fM{GaK7KNSpnim-esVsef16NF;wt`+vKp{|{RE9V`sp$$w!~ zPx!w-8r83;z`tZvT)^_bUp*-EUofd(A%?#>(BDow`d3V9I~ks1+5oe!12``xSQ>*S zKOVm&I5^mNIF{1bJerbKD3P8_Dv^GHS6S{d!{NiYm*~zLB%^{I^1HF)_p2X{*Vos( zy1E`79#&RXj*gCA4ULVBU0htu&dz@Q`c-bIudnas=H|h4^9Uaw@bIAOfnws3)T;N7o z+2Nb~y1Kgj{QRb-Yoh$(;^L~Rs_g9S_V$OD`Q_#1_4V}y1qIE`H^})Q5U9GkIwvQm zwKbiK5h?*vNS+G}BF7%=myb{1km7QJK|CkK^&RpdDI6qqMnMwT?)>@28K02Q zRuG?ZV2nlr<$FbnHV+#k;EsmZ1M5S|3kP)}kl^F-$Ez404|%TEocQ>7OdK4< zpvP=X?@mHeNC+h0O;<5AG!y|5@swSjx)p@E0Z9xS=&i{J^%V>cO&<*E>xr+TynJ+K zrWbpDwAfTzzPyPyY9|=n$T}D0t5>f$IOcdf5%zmB#bUYbjA{HKjrqO7(UL#0NXi1o z4?DBB!=v8N$f_fVp#W87C;|*%W?^CCOy4RzDOJqEndqVkqK*#_4+mu9z{}fIkGTu& z=#6yXHw69ykfWlKl9Hn0G*fH=^<}u6nG(Ve2!QNcFt~2;7qM}1aj~(_FfgL%i@S;|nC*R@ zrx}w2C>{oTVgs@K#^&a+Ql&hhQQ^siA|q@fI4+nE*HUgzZ|`#cfXy~Yc98QuiStTk!2pD)sczCNQU@o0Z zille9H(QCPgC|DB=SpuAeaZM?1D}2ZkBZtM36F@lS%T=c4ieFFD4Zb7Wc5NARV-o# z8VLqP!OHprfs%qk`0*yM>Fu`z%$v=)uX5QM2!N6?!t(R``uV+5^7Zw-g6db}OxNDp z-*EM?Ka^chROH+SgA>=+U!#=d=l320r{WyygoS0@P?qZA)emJ2mn(k>4UkXBb#;5I zmv?o2&9%MuV}AoWACMX#d}S1z7wzrs0K~WhoFpKH`cJcjC;##XhxXgHUVq7iT- zZz&;=FZ_2I;eU2$zv80*6^B+fWzKR2j@h>!l@I^&g_iR|!*dF(oC5@rN#7w=b2SAx z8hqx6zJ9l(6qfI;Bab4PYpJUqW5>tuij{A!uW7KuR##U81lPZRXUQD*a(CadZ?a-f z8#LC{)y>Gv%*@CjCMH%ao(3Gx^O)fN&4z}C7;y?h6j;A5eo|Wphs|@hH^k#JGc&!t zz1P=G!`s|EJUnmSIB^oPr;S?F5fc#!T;JS0-@d;m8d_Xj{PvA5-jVzM>I#lBMS5j< zSsFwb9*?1g8?>dtq8CO}fHt^OO%Egxm69VnGQ;rdl_hH` z;6o9{M1b-&G&Fo54DIaf3=9PF6=@QNuCL!yt@he7`15C@y|AcD#S9Px);Lfn4V#&o z78DlRx!kNX|9+p;)XXeGsc5ZI& z*RNl*vig%eeE;qC(7*s3%|AMR>GrU%-3qps%nkxTtAV&6&mY`t_~MsP@bTlv!^6X? zb-s?Ub!~t(l5%pWG(<#1^1{y*fVN6WNhv8|#2p)Q;$R~9OwG=UN;){O#P{`y`tVOo zOvvl$$?<8&G(E9XFdGdaxVf2|H*PPYtMG}XZH1TJ!0a!u>)J{{Tm6xuRXAT#lA*ogB0Tw(4f~E zawHT|74sw#a#-`l6DG(>+b&naeyp_cLqNIGM7fwbA5mWps4({Jd`REh{dPnwnZtQW6dhPDgJ#U}AvOGBPsK(+NpPNC060_C`o} z06Ymi1SA;1-gEEXy|cEqwz09XvMQ~ps36^v zg9ETW78VvDIbdUB)!6XE zYk`4*KX3Yh+I7z9wS16$M#Q?b=kfP<`3@n$ftH!NRxNb#7X=!Ouk?Z<> zRu!tL{#!h4z~4|6)YdAuO<;~6zP}YPB0*y`r!jyK3ZnsD3=Fe-jv^u=i3IB4_4Oq5 z9F^&n5LRWV6U@oANDmAJm6erQS)Hnn0i`Mjun`_>t~3U&5{R~*o}NIt0Ow?7Be##g z!g=sh;v$tHJy1~re4?Y1x+hvJLCxFtUk^y^Oi4+>!^6|-ef^$qJ|DFR$_M=wQ1o*u zfR{jn+1Sn}{ls9jKG^M3at%~?T_w5}~+A?bWGC+v8BqOdS-kq+rt*x!RJQ@n?lzn$z0w1S4 zw%h~GnS;^+tP_bb2HRFD^XEMPc~d_!!mQaa11>7cYFe zf^j~r%^9|~w!Q`!`ug?mm+Q+%!A;MJdDyoAFOUFl?M2xKXw@VvH`&1yER?jSWOC<( zp*E1=YHOzhHj4LKS%IihZO!5 z53`*bp-3Tu=1+nHway>?=p~GfsvwN1AdD`8sSt_r^nXitXlW?8wZ1SdW#!wlMz_2F zt7_U3(CtD^AmAb%n3h*o9;|PM9UUCL7w!Qzi2C`sproW^RT}U!9jTs701NM|xcT@V z2OXWAFLL*a_ki>^WDn?fz<$0g*aY%SX&^0JO>Jia+>fU0I2JwG!RjbQ&*S(kI`nWsiWgz zcr48SAQ&Bcho-4^g$ZgieQo&^dHq;DI(5VRh?=agA1H_b1?7YiWr^2a6y*Tb zEA*ns=i!IbCh~EEMGHDOIDmzrWECK^z@*V0;9G$zEh~#5tQbLH^(?sv`hE(~l`1(1 z;u2Wm*$WFANdQSObdKIQQMB`HxGHZUKH{lVS~7wrppK$wqxMHe!dssRZpuN}q>Z?d z|M|8rf|Uw(V_^p-53nE$q3$KfP??7v^coZj+5qb;2t5lDcXD(4VhK6`!v~XW08JN# zB7<2kf)sN>Lty;5v9!^GKlaJ?dh%|?1Z|B#JM{dV+lJ@8_+pw2CL-*NZ4Nn zoI7QWD5nC@ykC=B9#L$1{Lp_VuU!Xh<)Hzd4Ymm`PUr$ zB^8BR`p0=ytT0+{WMZ&Ms54?P2ayC0)nY)#U^J}U-1}@QQBO*`uZ;D?1_WmjO%=Y9 zedPor9z!}w)yUH0jV-Rp0})+hCT2IpsF+tc#-V4{TGQD+oBi+<=45wUoi>iy&x< zA?TyslxLKU-l;f=s91q{KN%57S7ldashD&{xg=91<>9D2H1er&`*a1kGgD6#(Oy8P z->7aY%oK;ZU=fu#fkLeTK>)@h2?BCZD^L}9mJwe`<&*A*elff5UjZqidUurpkn12>PaSTm~9a-F^~*RDm|6x2}releiKtt0bQU z*9)ni@Uz~Q7M|ns&`rMMN7u>%g|_kirh1UJnM#ZD{mxSXDuDOsC~|gkI#oz01ZlTC zDXa4VV%E@VF8(4&{+8Y=FCmromj*vVh{!QF|7mc2>YV;Dxc)k<{g;9(8w^Kumt<(^vu5!T@NRGfC&>Y@2vY`92$RwzlV_j_f8c34-TLIubz+(-WFJKdzeA#yp{7-!()_-G;tuwl68mx`M*(R{{x7+hfZK{ z>%Z{8Pov3yRHuJtJp139F#B!yy?>>{{r7~~-xFqkPni9Ge8TKcak0P_k$)Wp&CL7{ zkQY{9eD9x)SO3H44Yyy`ukL_KGSXA%j2v7xC{;5vIV-qJLdI*? z(`wg1K|yKPytusVPzUf#yjsA-IiJSUysTz@Lj#{W0PkGUV&~vk)c_DhE1LXt2vqEy zJ&6d?R^B1j%hmPueIsYo1=#`#*wR9NApCiP;r)HuVdI1^R3+2w*^&?i*42{M7_?rM zbBPHFSoC2~ai*M9qgj#=FpEEttj}(4rKKOpJMMyL;c-)|q*W!7@c=|5n0*EX%$$FC zD*-|EsMFs}8wUPCfA8?{oGl^^bYwxw_4 zlm8IOK`MY(KH^ge=PLixX{je~@P{1vx4oYK(kKP6A@J``f{6tf*l$}&|0`bN zA4e(vUfS{Rk5Y{LfRpt9c{KUC_x2w(<)7)CbNDIb99833FOFJHUf4qFF z_{dgx_a=hkk*n_TQo{IK1%{ROD)L?j$$k&7EfCOp7_MQh`|y zPW;CQxM!E;ZikDs#-nLNGZl2IZM0&BdWmK33ZbRIVUE`|LIc%>utpj2!h8&(iM+I- zZtB6pyqsuuUS3yi9^&)MZP_je(b0?7T@7c^S!o{(s>n%@b2#qHbtR9Co z2D+tEJaJrZbmx7p(#KDDlOkO|%4pe|@-TOBIj!u)HxXG`IJqMl3?izZ zJxgr$rhN7DBSd1WKjka^|K{5YX!`fyQo5aEuj}z94V;?1>&S5`o3*i2lMvN;?7=O? z81#WJDPN83+k;E3;K@h?uNcRauxlg%e_NW?)dMPNrNb&|^!8=HUMEl~G3Xa^P(7de zuYU)UncW7-U^n@HoUJss@bHBl$K0wOTmwicoVFH@$R8wTSDG^J?tbM;J!d1IDF9qz z+k=NmgEZu7&N|PNQxwyro4iG+h;_u&qta>2nYSUFyJK91gPd9#XtY=;pGZBOu>(kL z_>fl329z}feb5N1GoqW@tr)VoWH+%aPzG9coE$oH20AEsr{rvkV88IQMzN%dGw-KT zda1&+&mw8VRJK#^a>plAs-qFrjg9vt+o^|Ld{X#s2tNW94@s+^L~^!vZ-lLkka$Fn z#j~cgdJ}oRQ*N4c$rKS1BeBMrx9r~&FV`Oub3GEn`s;kPNk5PVIZbh+KJUr zQHtU=AS}PI#MGZxzS`_5l_!WR0s#s|Rje8)#5y0?O{S%*2A?OjLZS*A=Dv0+px=VP z#;fG>HQkP%K{8Vk9gSzjGohk)y*);xF8v-kQI=7C2DiMxLMcP_O8@o{SqMs%&&25h zy}8Crato-;15psYZP1=?+=pMC6+38#2@-yLwc`2R#V{Wk0#rKyumGKtGNS_;Ld#+_Wgo!m0twMpz#0*?v{_D zaZ)wJ`yY+CY^#L+9m;(FHg6Gfk z2ma_1|Cs?Mwtowe{o7dz|4QiWoe5IZR6v7S>qmHQ7;Z>553g=yB@TSJcqUS5Ac6*c zfQ4n1^}>N{o#!KvfesFajPLLOj{%vKTN`oI&f1#3mzNg+%yw~^ zJ9KW*F8>93Usm?A@N*OJ0@hpsOBsM<96&<@ON;>6Yi(_9V`F1wB_kgn9|y;dE$4xA z%cyA@@LWntm1Y?L!e-sm$N-jmaXi+Yql=3R&}3)lv+4(bsFuEYd?NJV3BNp`zX5bE0P=xlZ3AIF_L0HV z_H(#eeK>^hT;Ix&XxZMT-kZKa-(-`SQL`MnVGq z&N2q)StPsmK_%i~qh)wmc2J%_#fkfN0l@a6qN2)5vP*`@)XTAHSzB( zkEih6#3e}uQKNrZN(L+%qkIORE(TC$WT{R_mK_uw`*RT)pPk<$+;D*L5?kNwZZIjO zprp9?)qsqlSQ&;e(@Gjsik-y&8=KQyy)4v^@@~^mpw$$rcED@w0Es+=z zVfijBtR*xY_eYLh&lhyX6rVp2FQxF{j1TTTdT}I3nzX-4h?mDZ{-AqY*nLBaHTyBX zZYT&o;nXwFi34l4zqs-!-~N?J>t!2pUbkB^!nZhO^(aLHk5q(jc0*B0UAHO90&gU) zLt3}Zhvby5wP`o+1%W?B+D(tI=dTlXXg2SMzI{_xU(M^jVaJ*!eC>B1ndFPe+jUCo z+E=oMS5ZB@SV`yPnOyC+!_`R2( zga>j4k@m@)%yAdXjc4&5p2ub6cxV2y=9En$yN+gu4fjoZ$)53}?g@00(udLZqBgTk zr&h?MS6mKMaabJBGaXP}A*37W`psFaCjWTbds`xc}| zRqw^*mM}o}H~JN$+@5gm zZ}z%SY`Y#O?`6_t>xOB~h*BJ-m~D>N5Bb@?R@=~ zPqQzC5Oo5VUzS6Qee;;IE;a{f@H>E9>XwGK@$DBai5tldSmF41=zM~7l3Du|JE+aH z#`AAQmSTeeinr+&Th^uC6yKHA`H!2?8H8qPUi3TX1 zR7Wjq_3hkUS?9A9gLe6Hwdt^(ZLsNzQFOEiDDF`6Lq#1V2-B=xt znMetaF10wDk}^ZouKqlh2BVSuW7_jF3cp#B6h}z4uCOl&M5*IY{kM{*V%QCew=lO~ zlNT*Yris_6t|mtfOUf23`@2RLvD_6tjH2Ap_8xL6H{|kED2uvCFf;A7Pc+PUdQMr^ z%@N_`T6T6l)NDjSkys>2TnafQ8(Y=wCE%|a*xl###}LD_LwSvkG9$6XyA^r#p}1E^ z(c6sA!E0E*9%pNx1=NIzyRH$8k7q9zvw$W(lR{^ zv4zVA>JO+4q8vQ4k?Pu}j%pt`>MTE-%m-Px*j7qa9a~_qOW{}(b;rc}rrQ7EsOObsG3RAw@Tgv@j1C}c`9iwdcXk<9a) znM|4IWS(dK`+4M?&dIs=IPU%3d;jM>pU?S-_kEtd_S$RsuC@1G>waLLLF59qg=30U zvN)~sib|)RMNDItFqy#NuGkz=k?Z$$Q7}>Oz7YmrI)8>V; z?P62aG49lymEU4{0u|rnq%FptJ@+|5oYP_rC4cc@myb2$9I9@&+9YF5a%>D2v4v1N z`C6t3t_QU_8(EKo&WMVKNTBM4+JIA;t3it*Lscp~Hg5`>6ohH*bk4U;XQsK9ob7pD zQo)4aY4C|kr_E$7p(>c*YcNFFu%tgd!7d<8k{Yqj*f^Q0MD}hqRKnv>YGgkUcb}fn zyycgVRViQDWVJ{rg<;!h8FP$P=UAA6Z?uf%4@a?9NcS+0vK0k2q0yTel~Jh<4xT3ph7XlL5ybV*CO$SWZdehT z5@tMCSmjj9vfh^LP_>%VEOx(WW76U3tIh*EI#ejJ@H4jli^*%2Ke4m&{ngv$5w?4c z{S}8A?eD=6aD&K&iO^SS;3&7S1oQ*{q@ar%Ucx(_TE&z*%bRJrxm^ra&8#Wf-QAnH zTL?O01l+Pq0Jjqp66~Y00NzbcUkaZ9N#4w57K8uv8{UNrKBmJ|iH1&B?*h+|nWt1=5j9;3`psftHpQxJIO$c?prce$4aU zCaTa!%zHXII$O7LOG!N&9-lBph%)GY|A(mt3TiuCZs8VMDcdnE9K`Flr>POI6)+b# zI>*Ks%`%uVq^gOcNi?cIGjk59y(5k^)4(ZZ(-Bhx7bp!Im_L!KeG`cE%$H^rB6gw@ zPbvt?2UmMz0Pdxun?6SzO(M0~_UnVMLzqRuTQmuXT0!a=+(@BD%tzc10M-4V7A2Z9A^MNJR*ox2uJFen>CxEY}D3No^8qp$UO*GYK^{62!A-P-Sos`i#ScUfj@RJA<5^ z;^B%EodvecFAl|=w$oTvrWeJ4zK8~x_34#bZ6l~t;Y3dzWXSmDBqHwKaYU2njZZ=O zAO#%}-S0i3KD6Y|wt>?4Y+5Fh8H1W6k`YZ0EI5!eUEQ0{G902`spS#@HeF{;q7o$r zj6Xp_LUQ5+X%**j)tjZN6<*v#rHqG&+71?kzm82z#PD~1yXmmuZ~N)OF(>tEUTC?* z{7TVe<>VG=$S|4QIt#2WT@ECv#1ztDp-n0qp{J*RQR&6qn&&N`8V$xA3j)jhT0{edfr-iC znKvp4V6yshb zf5ouu;Zz>dupkW!&;z7l`EN8VJDy(L?n1g-!}1rl+qXNM{uRW6+-^s1w|bdRlNNoIH8g%b?6qRE)*}T2g2ISns?NMZHKxA#e|MJ>^uqOTFxH+MM zUI6O-DaXA>{n|rP3#ng7{rWH0FW7zjtY5o1{L0Sp>s<=?$n-9cNptSu`0I~x8!iU^ z>HmX!naCmj_8leuWmm@kbFnhA0?pp<6#T7tXhlL){cPt86fhe5*@x`rv@qunAdQ3d zPqcja?lZQ}G;r>rzADm({H+8TX}pokjkNtpEkJ4kQVWn;fYbt{79h0%sRc+aKxzR} z3y@lX)B>ayAhiIg1xPJGYQf)53&_#Y?IQnRk7LI&2%NhqgTTV_!#+9p?t92_?%@dO zcw1Y*fV{d;@1g@IF1A&v1#WnOxJEvz8c8fe*ufHRn@Q@y2l65o-(HD++00lAxYV>B z+Pya9z@)Niy1p{$(UO>`nwM#{KC?2f+LE}LZM9xDKibWtl9z73zScLNGTM??Vcs<) z<4-7JZ?l&9NmW&PId+uZ!CGeU%F}N0ymvDvD-3;9<(54>=pF2p2Wtff`Izi-F9yho z8w@8VXX~2qZ1T&iuxC8a`!wS{SxmM9{mEfof7wNDuwB!7UIu)!^3k?1cE+-LhITPV zV11;1cvbDk5A>6>SKf8hL|6%X3v&dlrY>iHn)wzxbBrQDa_N9WC}UzSy@P`QBgQkH z^4=ldn#F=2K0Q#GwxFJ|`2d&FwJf?emN8;Vec45IDy?FM&rTi(XB^*7g?;(4>Xg@X z6U8)L(%MoI@8(19wF^uX;%ak-xywUeXR#O)Lu^a(3i4961}w?5d=Mc{Qn~)cHWxR5 zf+AqpF5E$+OX6I&*X;fjZ95F^j31E^P6&)Tmo6KR z%-SW{7P=0~_$4lJE|o5=U7N9(!D6gdnQMD_Y}VR;kboc1c_^2efAeYfb6XVfc-pL- zLXALO<&m{}EH2FsGky`Ifbfjx4itd6jv7_1tw}YEUFnveXc+=D3v4vjSx3lq2gs@V z8x9niEM$c8b^*Eo+btIauzpKLfF>2d|&N#&LxHo zkHKY%iS>ad)hW3(o0tQAUr>gR^_q8xlN+Av1q+C^y?W!?=g${-fJ0CN}>lc{g4?(s6J9tm77O{wJjP_r(6*81jS~Mk{2C0!o^0sH9dl(MQw+{Yn@fY4kbGk$egY!7nP<9TV1<`3fx?O2?2w$V`R5uBRk-FcRlq{hJ+#@;_Kc&@TKQxcE+ak; zcq=3@qRt3@qcHNCCFr$zJ%)aR;CV10x`{SQkWT%a0wL z9c<;vd*|1W^xEbxDQH_OC{r+5dYD<34im_SZ-#eBs@N%=18e8hD{eJpY)@>V-Yd)p+_r(oE>TnCH*}%*?yB8hd$^EE!QpUKajDtS4nW zWg`<&wXp(yl4=>YoHZDaID2RCRUeV9mbfwG;!-CZF09$2D!c`f88Ar(e#T*nuUe2~ zf;ql8&<9okT_Ds^nYJayqi?nVuzN37!v16;0#^+1$>I4 zVHv;nWuU-S-!dVY5VpbxYn?*Khg~SYp=oNDos=DSNs{%XC)X;_FTh2S$7LE+criWN zQd31(t?^Im8mnfKX)lmBMI5H8d+o4hHV!6v&84M&OaUy89J;!k0}IUsa)ofYmvU`m z3VFf28^ChTEZ*mLoGfxHv+Su@J?<7eozjJ3e83{uMMW!MxM_am=7uJ#eX|dx>#86B zmR&n9GHrFZ_X?X$J-$jYTDiuuU-kZ(S4*~d_d|fwa5~BVvG!D9@4muOt2v7y{-w5~ zYxfR1alMDBudMvGVs*ooA59>%?qx~a@P&ygjYGxrhfDg2QwTd@LJ256AJ@79S?r(v zwqnL?)9j@>=dc7O14$ZXwwtga-6U9yJ52Wv6(1YWGO^1{;!RADBrF+ro=zcOyO;UC zW0SP}ECoQ##UFSf#T0{(p4XN_ggaX=sp zPeqvE?)oIZvT%g3iS`8WNSI8wAxV#a3LAtcFuSV5&0Si+WOdi#061_6H|7che(5qL zSkRSEJ?SagaX<;x(nPVI2={B}z)S(I1z@Q=NrXPY{sC_ExHjIMC^iMQa@ty%*yTA) zDv(7;6H;{QxCGG4^+TG@0vmH|{0eV@5pN-EB47(}Z7SsqS|3pOP`**t!qZ<(-HrGi zVFMv#8mkR6o(j7VND2aGe2=SNn+G|j{>r!5L~kJfkXZ$UKJHCtfW-(h36L;BvN(PP z7=mEF(a?(i`NtH-K$Mnx*+O`x$5yP=trdX&Y1=}631&*g*cDEoRkS{f>yLO73UKLM zTBgy8rZM|xL-tvtSp)+!0$z$)48DhJ4=tkbV=ITFZi{!BZXYT(XG^6vR}sD?@cKna{I9g(*%QA7(6 z0w>6-9)blD*hsL7u&x;c&`DI7!33bh7FQ1?RX*EiEp5y1av^M6d+sj8VF-kxd(HNb z8J$kaJO|rPiUvwg;6MN&+cP?gC)cmtId&z2Op%yv0`l!no%9*}Dvq7>r!EjFs5BpU zWB3C3uq%+g1ghmn^f;vip$M0Oh*R z$q3Y#u~u&?1>!iYHg{?>!P2kY`vNdxfMNN1{t`X!blBt-qF$MFQP|0qz>BlR^?q$W z_kf95Z^NnnS%-5yu zX~4$pk`unb=Gtt*@DlwkKA;;6&i�tTiDk0=WjXK44sdiGbe;2>9^6Be8lIEFczM zX?=&gHjEU26*&kC$M%eXIRl*P9GFI*vPQ8EhG?aMsFY37ChIl7=add21IQLaR08QE zJJXk=v9@_?{|r}%5+5q;oWVE1!nhFD1389<2Yd$;bUVr%A{h{9USiY%i91Xs>|h}( z?VADTw|w6L3sPI!3q-=Ebm6c7{iu|XVfHXsgUy+d%I1QRK0jFS=W1N!BF+eUeu^r{ zen38Z4HBORup^i%(g9M5;lJ4KU+iiJv;*BDb^!Pe`(|K-;8`0i#8JcA@qy47XuMI{ zRZHBB0rf3H?hcqSxPX)-UpIf0rK5-D(7|h~dLtdkn@G){(wLzhyiN81)Ks_1BwtTW|9x` zRRaqEh+9&?-8S@Fb^ze${Z9gQwD1=4>@y7bw>&q{z_t{?6JTHga6DApw4|U4A^?DD z0QhiHeogok*8!DZ5_s(I0>(l7t1v}0nFB1H3|Y2P4DUu~jcmxpZ7Ua0R@iz1{(r`V z;x{oz1OSU6G$!c`VH4Y49gGbm1K?KJ$MZv>99JSxGFc#!4^=Sy8JQq8hbIH9H4JWn z(!(@PWyT#Y#2I7B;8(U^GU)_DO8%WW5TC#*0s9M!Mf8EbgJUeGeu*4d`QQG)BVaRO z{ej)r`PUaA-D)YT>^#}&UhGQx8;u2?h(rKpRG8eB*DD-y(vli%oxqD=`?uu= zfQKhm3H-(T;#JLEooT20fj{8uA0SBU0*4*2b^*`}T-uTxh=gK*YQY1-e+f0iH z5C9jB0)PiFGl=8>Dg%+bG0brQiAsk-JO|>iE-~^=w(o`teBEv8B#68qR&IX*3e+GV zS&2VjWQgPHJ#`YGC?GvxT>*fQZ7G2n4!(613PAvPE&?G4hHH@q%ugUa;C(>X4i(G5 zksN^R=}@F53%or92Rq22SwKeSGm0@b9KCMEPT;jm&^jKAI9$rK65#+QwC%y+sh0tF z4TBtMTb6W~#+P#3OKoNv8Jq4fjR0fAGy?Qt21ENxiOF!(1_dHE&6ZxX;@5d0m;qbZ zEq@NP`x=NtV2}&jtCcw8bHXmOIj|{8yKoBzn3<#f4+pi5-46k<0r=Y$KdPlRHfm3n z(2mDj-WFOM051tDD*+6X{!O@q>%_6%7NfVQ16wvgC5$_QU}yaBH?b1%hFf^uZyK_L z)UYwwj-^cD0K_Hy4g?R|J~)#a9PX`y{HH6xu0TF4w?7B2dwv>3RhTfQM7?55N{ZtH zYd5g}z!-pd3UC~tKUl)e?J|S-MuDtuVf7F`;J{yh4^pf(ZGV(pHP~^Ay_gqR2SF8# zBgn?D0Iv^g02D@Th3_j?%->zZda21KE96d$4nla)8&L@t8^R=wSZ&*hQe?$_E&TI`eOf@)lsf)U@?BL zhAOfcis3`NU4YiX*v*2Ky-D2d4a@l7=h~BqiwcjEZfL%c54=@ZC8;GaEer2oneB>=q9pjV%BO<_1pch$C7+Rv@VoLU!L{4IttmJ_E*Wg3>kAh)WDu$nU-+8F*d5F*vf?N`L_L z39=<%nYbv!C=|lMyMV|0U2p_~U<4Od=;f(37lvH1{#DL@gUcwj$jRrGTMH_FnEHsGoV0!>UICKFhf`XwRU_Ag?ATpFZLJ_jm&0rq7}-(XA*qz6XmK#M_A2P^`>OwONH&#wA`gw~e2ZTn+LeBp2d2tJ(G zxeua^lhahJ{D}ZFrSK*KGyNNU!vM$6AO(X({Q<+x6$AKKSjs_K<>7@eDB}QyqChsl zrUSJ0T!}q~{x~#wIWo5VQLvc9m;o#_SR2SQaV5g277B{Mh@15wmplSf`Abj)^a9Se zMQo=@ZUKnCFH4MxhGOH6!%bi|wu7RRFc<+r5tN*V(Km>SKrFbFZ+sum9hVr+V{Yjd ztU5p_YrkcJ{2riGh%1qiX%t{T)Z#K%HcTi}2s0B_AP75PZdrP+YB=C1M@!*Hyg+zh?%plWSV*|Mz2-efdkMV z!aE>W0Q`OrjK*Z$5tp(+$no8e_X70>+5>W6@E!*g=2dPHsz~fYhI!|NG|+J{k-Jcc zxg{Hrlh}$y5wW9B;hYbUVuvXZbO4pF3I|C#2oNkO04{*71;9^$EJ5-ZIH(gYzJpp2 zq*Yq40L~nOpj+|*Z8QKV0k{s9AH@Z~HU1_DHVu3yTzW5qAag5e3g^GJL>Lg;0Jg!l zQa+SS1nMpe4%3B>R6AAu0b7AI(Bw95wWl%~%=_Vnx$~0xSdz$wxlqI0CEUZ`X#T($?O5!RYd06(*?;Jc zy!-yMTzk0g4*IW4yt;w+s5c7*b>WfYE47P9rH{tB<){^@Br(V*G0-1R(%4^eiLJ|D zm8GHrE2|C9Sz)rLf@d>q8T6(S;OFn^?!Nr84wQWdZAoWmO%X)4_wOG>L$S}>+}PLv z?FK<>sG*@D(AeqQw{LxYeQRqSqx1K9!$FaMP~kr^k~*#3&ImM($;(SiO9R~jK#wF) z=zXgx*2j-SX6NN(Wdi~O0|NraELOqug@uJ39UY)gz}(#2=g*%91_p|Yi=YQeJ{V&GWAP)JC)!C%rlfONAWJ9xjaF+banqf`2!0TiiNrPbRlS* z6%Y^rcmx`Esj}Rr6}&J7irNP!y?=I1*l z4UJu>@9Y=3nhm-}VWukTgMJ7%ZukmCE{~k`HY#SMBsh|olyp5_mT!3-77;U7~C0uq1taHjP zxaMZURTu*gZ@y)troMdAHwyGah>l*j_Smd+qte`Otqyv6iHeGvn3&wWdGq$|lj~<) zl+Rm;E;(|YTDwg4u|%GLm*t*t>v3VO+edEZBh+neZExMWb?;uBJmc%U1GX+}(Hb!{ zW5k-5Hi$I~^9;buXsHf}UA@}#@TBi)T3VsgM-QSsU@{;aII<&9-m#}S*KSU5a)KVP z+fAeY1Wf*2-wm!koZoB;cD;F29nVFWh5W^7N{rJl9DQj{(hf99KYiu+_zh7$Cgz)~ zPZ(G3EL4q+>!(M=#%2y0#I793{TAM}tUaPaadC070rgGb%48C{K?=Iz`$q=Jj|}TB zzi}nM*rJ2>?R8ll`+XGi`n!Z)%35BQ4OSR@55J5Me-^5JDI0P(nov&OFHnJr>Ai}w zW=?K?$azV_7su}(Vh6|5+`A$)#3Qt-*%~n3IPqVapTjzTIpSsG+<|wIiPz6oH#gkE zCSaCMJitzQ-5G_Q=6cb=ChF_xuWnGCK)n%zXO4Lz3on(~_rNJZKX*5xcUNA795^V5 z!hZHTtNm%z`g_=ytFQD6+J`cpOc9?d7a2%9BcYk3BdTvgz^yNOVc?@X-#ZPTd#{#H zSIbG|Y1SrJknz@ZITc8q#tHAHoxXcgVd^uj+*B9`yXe9R8XF9B$(KQ=MZ4cA=t@24 z;eMH^6+#-Rnd~lHLChN=zHoZkIfjGSnJ}e-Rg^sR!UvNzun4lPrGi*x~v9s8v0ZLi?%K zsOvokD>u}>xn6mX#l=xyOMvD@^TjQUy@uq*i`C0Hj8`faFr);&vYmN$zCS~NxA$`)r)!eQ4aSW5-!NntH{!; z|K+7u;k5bB`n8*!5exgTt#$uO=~eE3QO@Wv1u|y@<&6GzHzxlxQ(nVBXPDYKH3#nq z{H#>FNzLsw5zD=Y{tgLq3tH;SNqcpZ)qAED?5UD2x}`w zHXS(qMYVF7{GVMXp0rV!j4529Jl6dDS?VWOEdOcyD09wZ&1G+sCZbK4w6Yg-$fFAH z2OS*sla}|nscsi?aMbvdk5s{Gu=GVfk!APnL7h*MQp>mK``0gl|8LUw+YQb0`VXJ! zU;i{ZDlOj~Rk*@&teKS8dEGnk!6E*FX?m;D*dF4<kM42 z(a)4sv;qTrK83sIQLJT2U;i@aDaR8&eZS1h4m+Ie)>Mo5WC81n{o4^@yG8HX{OC@_ z27>?&WyL&$^DDWrBbu8kQwK7nY^rnLorEt`+upBFv8D%r;JwXZPQ3t zz>}W38vO0n0f&90OKQUL1R#h)iuiM!i~tR%lUg>_ZjF6}80b^Y)mwd?N)oMg^Po>*Apjsg?BFWWi?FSPUk6 z$1iXc_~dIe#7~N`WY)R*@we)&GJ5wk8$WRFoPN1*`H=FX&a(*zgh%Zf^xx_L8l1;a zI|{`P7(6HwiuJ2{`LK)g`MAZ@;i-rZL&sfn?!=DUC#!dVNSFV5?deGVl_>*b-@cPo z#ZkgR_!@FXp>tkKnfk^rE%fix@~HI4>H3JIq49*6De21(Mt+sB@Xip=RJ=t)Ji-$yj9mjdL?auoTO$_lF6 zkJ;X^sm58OFB#{SPK)G8loqlSVPPK0txIdNw{Or(Z6PC!)XOi}Na^!IqtFc$S-fl> zqddlBoDQQ!RTn){)VmWc^fTXY>XvI#=(c`v5+$*FcO3N9AJxcinoOruz0Oxe87hsLkrV7Hud_%nEW8vSO? ztK-@@s#sks2|Vkx$I}=xO+y#SBAl;{Zq^x2u3-{C$XiGea}+k?T=NjVAJ}IQxqxlq zm|~SIPOH44(y3<=)7T|UCUCebHb>NWXxv~j_VTfITbq}riR^yf-YFxR4*eUlr-()Q z@>jo}6B_QOG7Rra@R*22q!5sf8E!1*y^-J-Y0~XBe`C_Btv+3s zrzbRT`Q>9(%2zg7EfPv$*fv_m9Aniv7N+1EEo1q^QLGixJut#nRjWD8V)u(SCLON6>O8R1t`2-A=J)XdD`Gb(OlG!UtM;(4 z{D~yyZ;$1$BKDBPM4lS^UreYhkrUbE{QWYt)4q zLGQD?25PdOoLrbsvv@1o;6cc=k;>JbSZg^tJHBF@!mpZGD>phjxMKUBKO(VKdlX!C zuubNtNURkeo$Xz*P2!(SthE`Pomk0E;jfJlFl5(b>Q2nDObF`}9PBys-M-Vn`*qg_qF8;ym?tzDfyHuWD zs=TpjY@T;gKcqo`FKq92JVM6|?O>F{4j5kta{YLlo?um7L zCXwX&-s0PnN|l{D!~%oSPs-&E4%N-hjWh!;IW&0ns;fYE*ps5XCxSzU+0?UiS-#68kIJW0@%1Rx zv1#I*T-cu^86X4>ZY>51*7l$MFP)USs(H{|jLP>6%?$dit{RQ&)u1ypEDr@=KPiVF zLqF1dq4302`EF5!9Ys8rwcteM6WYG|UdiD$#U)Ppp>+2!9<^kUVJ`AvnbEUS)S=IsO(?f7|l~5!Y6FK#~mad9l;}Uj>698V2Ao zP`?vKQuX}7a=&$ymJ9snZF-I#0pIOD7i7oR=gs}TmB(6;)~B4xh216T5neN1;fYJ~ zhH{llG${pZk_gvuI>~UCfE%b-4DejwXTdnwL)Eu@?v->r+b<++a+=2ESN?#BVh

l`lM{PBpYgxL;2$YMd9hx_mT4U{Lp!BqLdIWYJ_TH9a*D zM!Z5%a^DK-ndXycsnL$ePJhL_Dqj+LnWsML`CXIy!ILCnYqj+oLVXYOzIb)0qTRg& zt(OT;z00MOX$0cMkSt%HfnORUnaTzrlWq`OY_#J>7FD3F={{1 zUuza$a#>lHuVx&N;UT;1!Po#1j2X9%t8BhrILYq6VlSVC8SQ`on;T(<kTttl9W=IESiC+Q9DLSW|d-AYhBRXB-ma+^5RH3>!- z@_e1ucU|6}_SB&~^^w=p8>>-8w@&-pzLjS^sUmo)P`BbM-A%GS*PC(na++riJmk$U zf2IOI`ainKa0=p|aHmbKf9ZR-;VRo1YEw1-Y#RJIp;scr!KkynB~J2(XyWpCaR=Fs zh?i3*FHc<}_a$+s9l#If1&RfPA6OE6ADq5=^_4dRLUH@A3T-clryJoxcUzzjm%Xjb zFlpipiWMo(h#C}!Kw@5{rHNzjbeY@9rOB)6_$7I* z$jg}O*i>-?_!+);Pm+*<3400pv9Mqa6)FEI{wz=Nq<{GwR5k#VZl!U{vb95-<74C*_U8(cmG-OM@x>li9vMO+rTN zEs9edvOIOZ*{@q%^D>W`iF#VNso3kYEX}AB0KYiq?yhjBoOzO;j zp+wiP@74?WDqxS{2Z3Pd(1ie9L!49rMq;Pw0v<`9#KSWE?b22-|Dbo=czW9^7>vtA z<&x@IRZx7x9TL9gi08wPKOANSYWK^_-K>5Y6UYU;obbkV15ZX67DBSz&K(*VxWDJ)T|QhoN=AkI6~0dzmT4U z%>FSoH|)WlLRPn;*~C)u(ev%~onoQ>?uUV|yoWH{ALLx)jDdTp@{JSZgn;Ge2uj0( zttuD@mX2FYLDoFd6^8C{zQUUJx7>LJ;-qJ48Jn&4KPGLVlvTDnU*&N-OV;5#;^p|Fccp|HZuI?r5GOBf@_22U$!=IetOZ>yafZ0%5xvS2^dX$s0m(ED#F)YR5)D%RB0Y{*MUN;)V>NJ!W# zc>DM`D0+K)+sotN;^rykD&XMYQ~GJHI>8N>cgl8-1mw z{}DY^g#2w!$n*DLKFqP9TdfAXgIR6Dkio;oww#n zk3|&nUQAXbh*%ou@W+ypl7|lubVZ-BC@FFQA4y8h%(B%}&{H5Ho zJX!NqOR5G_tg1?%Hbb|+v2GthJHs^`qO%OkItmpLXkr`vL2S<7M3Y8F zMgot2Q=O68d@6tyv}WnGU*<;A+qZ8C1BjBumb1LQ&ByUmdK8@*P3RPZ~o z8CRzQ4lXV(1VXL5^@-`3q{>02Wpxh>f_4p$TCzpP%~xs-o-(?j*Ok#nCnp^=&urR^ z4M)~WN@%pEGDes@zgFvB;joB!IfDjjzS!IqL80ALF3Y6%*l(jYGCn>p5V%1`g8up? zchYJ=czC$3_km}5xI~5o>&9Xs7iF-=!lSdYlmsHgG!O|v?iuu^UikYgS{j;~qBAjr zSaG(Ps=wn4O8=Dh55U`KZ_K4Hi>$Z;r z95(0&1!gK8-pmw)goGF<^d&ZEA(kiKRj~{0lh@Gb7r&NYtZQ+PDCiM-P^*Fbi4!Mu z6B_c?4mpXizIo@Qoc6h;<=RG7(T7OsjWu{KrFBIvpD4ycq7xD(1Y|t96Lv~K!&hd1 zwh_BI2g1SnONQw$9P4J@!!eNRpwXMhm_g-{D+lle2i~22VO6rans;&L`a+ano2F%o z<|3nvtoie9Rr7f>Z67QACRF2#8!z9GpVD`CCijR5cXJJ=)%S3`OX@-(H?pEQQrRI8cl;U?5ET z-YcwCNa7wfZtDc)?bXZfpWb`%syTN=pyP)QIAVPC)qcdVOj^{La6#9m(+H#8uXpO! zl^N!u&i7pD`mK(fMNZf`-jA}JdfgsBHc*mgDPlic{RmYl9)~_@e*_7R+T%x!<2UX- z$s3`|t~U}opmU}pyb6n;D?|BqR^*ZP!;^z(RY%LIgiiTzHasmO85SuD#cdq-P!z8> zdZhV;KuemUQA zCr{)biCLwK^3|@WTMR_^-YtCdp4QWj#dk_G{b?SysJOgfbhS|$-Mja+F5}itkuq-a zvP#%ECVe%6<_VAO3X4c@qi!gFEqZ1oc!k~9D6`WxD3gbSub5+okuHk+E@2EYip!rZ@{HtWoYE<}Xixm1OMQsjsrQOg;G@wMmw2Bn z))u3ust@HvKe>NQ6PKJNh$dONNFYIHtI14)jltnxuFotNMxGlN@+cRdB%EMXvBO-! z8Jj7#`K4rIqz}PkC4rov)S?ezG$yz+iy;K5Umn@dXvr{45gCRJ-|=WW#ubVndtG^0 zZtMaMux_^tF~r|oHdGQ=PJVou;_&2(W>V0XE?d0h!?P%9BkyVBh;eE!Zcy2A-MhC` zJU4~tpCICXKVA7IDgRoLMmD~GhyHEnHKB-y#$Q8Ygojzq(jLwu;K}4Ww0g5c2Jih@ zI7#S*eW;&@!eKkEsvAZ6 z%=`1IjPwdm((@HLi4$8advULi*ZI1h_)sE!M|VTLG>r1>%Oc~a_x&RuIj>381@#(# zomQskd(NruO4jA_C?59RDP3n=Tn#ber{@HFQYC+Sog zy^Ek;_J-g{u6G7c%E2}J!*m&&;Yk>e2ruWAPw{lSG>tG95g%E59cI=_xmoW(@Z~m& zy)l8J#0*NF#@BX}R|Zm)u7&#aDR%XSj&_pZonZq4jTl4kJTg|)7k~h1wdxU(n?BHf zY4>)Jj6Ve{7ED?WF9>@zk58=9T^)79QYiNY7NLMvyyU&FZFFz*W4T}U=D&W>UQ(u5 z88iJNKbW9p=~+DEP$&31LGZreUyQGcK>ZNPU zI0J&`28755uAFCi;%P*^c;4Y1rLYKxSn@Zb$4# z)<5_cT;Ki&6_B}iygR_Mn@q!=#e5IB2INLHGE4YNmhc~m`Hq(+SauUEGegyrx6fnj zzN7;y%O1kzAH^JbMP>`0{DT4>+&iAlW7!Ql-|OicmObF}A6FZBl;CdzM*jtl{ufiY z^FRK<=^MD<%+GLlH&Gia0vuA?HjrE#yN=pe_Y}4Ldin-wAdxEb_d-QZ!}Ur;aK3BJ3kxWU8Qw?sKmp{_9r&tez(`ex~H_xer&iw)}o|7vVR*gYWdGc zElxg8`!DkE7)h|eHK%@7wq0e__PDXjx~Hrfa%1;zqhf!&vCD$k@q7a7ZZchaySUA^ zhddYZ;x_W)HuB>3zwhEU3tY(VCnv|Yn{?UUPTsTaC0&L*d5=7Kk34z*Z$5d?0@qsn z*+%Rp<;Tnc_Q$^OmHmaI_iTH}`HhDTnxJtXcA?;`IF*4I4hn|;-?Pl&~=|-rs zsxqsxs3Ib8mpzxCExRlSExRp0S$4FJu=cUGEa$0euTrWCtssBTsvGNTq|5X{7(4oaLsU{8LnBP+0(O_vxr%|S<2acrUIrErjt)>g0h0Mf_Q>? zg4%=IgOq}mfF3W|lwcySkG0^hr&o&DRG-A-};pgUl+b`Rk zo-;qO&;$LP&1!d6Qs`f3{{FLO(f}7c(603TCr@4$A9$mIVGj6NaDtnkpWN8^aolKl$&+*R0|Zgh0QPAwFjr#i^^us_N=m<1X^-&S0&S1#Z_47=|R`iba0?3p08JSFpi9E{&Kc9LhT|LI@O|bmQKD7P#oVmvxa5C z9UdUsZseR!0eBd1{fL^|zc&nOTc<)tjrRQsx_{3vL(iayNz>rD| z)rH^En<|`l{3lakqg8+;j%g8g-&@NQ5L3PR*S~+eu|9_4`+Lu?%=OFCO@7rGL15@< zf?m^k>CWMgaK3SMd>gnZc^Si;6wnge3-MIHSf&yh?Kr8alEB%oIt9#W^Nm9Vjde1O z@#rxDF@7EL{h$H16tor(Gz>;#4)C4j8v1Deh1cptrf$EHGkbDQ4tC!L`n9_4&#^zg zj+A`-+JoHO95y3v{M_2%73XXh6+tow>aRr;(G)>9vP z&cCYWa}K|gm}F{fXkc)z8UI>5VDW(#eEkXHxesr@p&L+#2QJ(uyI~Up#!R68(qZ}m z$f$Q>Zx?9IZEOk6S-)4OS+Yn@?sI81pAw(A0%{t5%cmT4j`led0mNyE; zQA<4P=ui{!!v{U18_-Afh;a>+`L1A?4~@Dh^PWxjzMJKCQF1nsTB5hPOQ*B(LYV(L z#l-yle7$KcnD(6xk8Ytj%k%T?Zk?SSLSgqJWQ|5WR&Pm$7Gn3sCnDCb-JNcC^Ui=s zXRAGOEjl8=M)7=A!3hN=rA7}M@L6@b_D1_vJ@d${rzaTi8xN;@rGJ#vFU?ufFTT@} zvZ602ITNLO-*Oa~3;pmr(U6IWjg94=u%2ZT%M@>#3zS^PsADC@x_{#Qo7cK4l`$Kr zZv-CthXd(d?Jl|%t7MG&;XPnkLL#ChUV556ZUxC-!p+^%Bcj~mug4pqZAKPv+!FxT&%C@IW@UQ1rkVn4$ zHfgGzk9_^{IaU_9zTnT$b2m9yRu<5XY!An@*!Glz{Z-5{RoyNidy^r{-XO1dY!!d% za@wC&^#?mIkjnk6^}9(~|HiqoaqjwXCi@Gyt_jxW~V5}t86h`e#OGb-8nNI9}_9nypE*|s1> z>u($dC%@u!#OYK$!n}Bk&JGD!8un z&sx8meBs_s0kQ8PTZlXbggga=JO%V`JO#uIg_A#9hh3#v_jntY{a=)3Mc#%*V(Y&b zIQR#*VL@tXN5$SvCiREoeJp<>kNW-9QsU~SdX&tp9DB&4M*Dkk1l%BUVIuUE8aT== zECKz%KPl+qhL`Y8r&clL&hlnjZf+NYRWoafc6aw?ZdO)SdwYAth=GAYLSkZKLV|r% zR(5uFdiqlM#M;^#INYO@+1S|V=jYeo-=8=F%Irc#cB6Q>xLU#EVFreVhL)C=+Sg$-&Xw(jpKAN~J0(E89gFXlZF#TQ4hTUP2_VAM?Doi7NCF^PY~5 zjH5%$!4N?}#JKG;m7Ubi~wHSy>Gmm_L!KeG`cE%$H^rB6gw@Pbvt? ze;pGO^ZGR{9o_Uf;%E}7&9+}3d>z6p3f`hgK-3D#;+B?`Q6uIfZU}+bqN7UV2kDlA zu|=s{N6j=eH0lORNyXTUo?Z+jF-0W5)i409=~#tEN=XklIHD)j*VoI9Al|=toL~Oh z`-+-;8zV%UXMQ;c3kwS`FE1NglsBNgXia;6(V8&yCXHtXWBZ9((J?SE(9x3}m!BQm z5JhP$osoftk2HhEtu$L-U+34t&AkRDJ*Ry9z-)=*InDrlc*NY?T*8q$<|a>5e5d7S zFjaB)&|Yt(K$Mj?gVhD=*V$R^{r$rTmg|H2q&A7Q(1boFBqY?-ND$ASL6yNl=rax% zdT~RO?F@2uijRzp%*p92uw{O6DCV@C# z{dD1&lX^8Tv|M6-rD(Epa*H%%7|jJHK6#!n@zjVYEDj<9c~H7nTwL7TJoS|sB7)_s ziAH_t19}u#XjV~)THn2UN6)|j)>&Y6>2e@RC8m%L3vE)_2t7Uhi%Ku<);w98@)VE?Q-=XRu z3tZpuC&a+ppq7TA zrq)4z{)47wCR*zH2d(x$l<78Xmp;nxZg?~5xB#WRuVci43ZP^rRXAl(c_}W zqeoilX$H=7y1ymCXdn6bPSVDS6Mb#}g*H^&tGra$=(la!7VhwHTL* zcKm=$Zy7CeY|!w z4`X#nCk?%0mUOC-0}Uk>CD1PHh*fY3iz|MCE?H zG6p1>C-3;C#6MLxsqJO*F-~LN#61^kJ~yW9EWa8SouQGA>)yq%Af4+j(2_bwI(02c zH8Dd(-QmEJ&q{+WiWS9;xwlX4%({k!RaN7UcAoiiwz{z8bI*Z3Y_<((Zzbo2GpvGsoIau6v$;UC-k+8vGYdVV5%(2}tM#Hin_|hMN>?9!JzU#t-ZGnwO7Z)M#~${dgx9v$ zT3-BWjAmV;_~O0x%Tu`fal|*mb{t3eIt!V45Be)e999$x=uGIv0j2)Haz`xn)$I5p z1@@tVrCai1XRVAsN#U;CS>aR?VHXjyeSrk2;T()kP3@@A2r2+}vAMbl)h3 zSaveG?Nmf>;T_Yb#B9@hhUDgVov^{W_M|7 z?}^C&+z~()eJHr`(jn@+j%anRDG~ze;W<$;(=0*vI{S3jiMJAb`Q3A$B`*TzuBCnF zAYWn+I!=+hdh>|Fmz1N|H6 z>Az~Ae|L<1C;5o__!kW%Zm;k5Ur|BYW(3L@{H-iJZehZxR?@UimsO+%`U(}4SoNzi zEv+(3mFZFJ%4M_5%jb?%EW4S@zu~;#{cSWp^{I+_qS1#=S@iAocl=f=3B!mWq5iEV z`01Gh-rLl%+|Dn>w)JW>t@{pC$Gb}_aXDQ80$eWneOleQ8r)hotL<|LTR=w zuF00^=$|hGl?NKH=k4beisAGe_jbQFn&~^4*=QNNHM%b?{J6vuX2KHmXR%4;H&3c# zvXHz6E+BwYzf|nIvH|5a3?zIhjat%6MM>BL{wi2H>&`!$t=T4-8rt?VuntBRi6Y-~ zxcI^zNbLDlSq5-{P_Mb>-@ZYhfK|P0Z%d`ee{W1F@Z zRMLUihV+={QRYauUzH+w4tZ%sR?xi#?@aN&?KVI+Ayp6g)@PbzL%SqO$Ol^{zW@W-E| zl-FIMsZYc9rfR}^qPfWm(CT~BUwCEgkBH}pVr6bnuO;;=qd(}R^_pZav zvMNrA<-)5}ds9O((^iT$Zw6RgS=Mt(JJBS%G+zkG-*Fh&>{uq%oS zJ+x&bVkR~gh==W55A6wRZ%pp9Y;b$a1fm_=T8%eNRPw{=awjSA%yc%WpN%o3uBNi_ z+L%~Vlua`OO{}*&mKBq+t4#9w@l$Q6M^{#tl&eD8Yar6q)s@agJeWf))V((3Rk9O* zfQ7g&=sTI~GVdhbjPmfbLCq}eNmp$gcG|!Du<3iLnMRZ(IYrm1d!MwS6$$$Wd97ozm2uGGjwYP71RPV;)M zURM3!mSpyxi**-$;O_DZaww)A`{LvRQsIPNW%`%wvvJz$iFNdKb!om|=PJa?gYzNt zgY#dU}ZuoJ$%dyF*cpCOr(1 z+cTYa|MCQAb1u)uxzzr){Z2c|KB?BOmkUCV-!us{yXJ?IwTSqnLn9)hFl*U+khEs+6^e%ezENMd>xzrjoFncJX7S@khUpK)SjYljGTq7J z-#uQw^U#opYUt?kyy)m6pSOIs`g6V;az?Oob0e3On)5085)czw^_+BTM$%v+xf9^S zER3C={veC%s$!W{jNiqW8eWX&R!+RwjG**`o(3ePq)QhmlTB!o6dgv_USSAm6i}_h z%~P498Ln>yR=6c^e-90S1#CkP<@h!TA2&TEEmfMnz%Pm6S4Fbahh0=S^Q=K?b7@jU zIUweaItdqogF~2|Ci%(A&A7fvhbFo6!>sbAPnJIEpHDO%Nlr<{BNqMbazei#dzhFv zQjmkWAce2X(Ma=Q3?^2LSK$}d`)^V7YX|3kht?h3AS-(n7qSj5%g9VG{vkkXqtf8w z?B?7+n{FJy0p!3hsk>R*Q7Y}}w$)`;&+}RbUwZeh*I3_#*;-KDF=o+Ka9|0A4qp_# zg!1-}#ZS-p{uvvqu31@db-jv+V8NokH{kfaa;sv$HUeR|@Ah+BKeVoR_f}wjb_q?g zy<3j5^L-gieZKo!lMb+qQ0c*$CYB*vQ!0YH^#)6p^BJz~TH1t5H;~fh9@g1@#6hU5 zo$#6Ju+NSj3JEd0YXlpn0>#>z6V;79UiW#^5S4|-egTV{p5 zT3ht5#nU7UE_fF?9?F%6bYfnP%`g`&p3A=Uw8JH?imyQ7K@i<^%R=;j>jT00|?K)&tH zzc0(gBA$z!3n9=WBnuGD%=v?mLN`#e$Ntl~#uO0|5#+^lt>D#GMt@rrlqOCdg@tbN zm@cfHh@cySwcEFCZ-E9~-mVwHt7yx4Z6~b279-{_C#dz3oVmIRZapo$#kL|Io}QmK z&c^TlxT&S5l~mX?AO%s*j!!VA{iU+-*Dt}07p0^Li8#{9Vp zoJcJS@Hv^*OTz5ZtW7tMuXUzE#W|<<-=j@=vEk*Zi&MVyiYkU1nWk8a%fM*!zq-3W zzr8%n&Q`&gDMk%SB=cHbo9vZ6z{qlOb-p?~AZVi@xlU1T>9rAg_Pd26To7x z!`d!#Fs|qOc?4d~sg|`SUO!t{OKQzRb8||2^c-!;4ay(OA>r)#3deTBh6spb3gt*E zUs}5JEFx|d)I!@@6*|}!(8-f;8#kq`R4yyeId?d&cP~RCtwKqP=4I$p`EBtk&W6`% z8y)>f$S+nz9uSyvmn+X^OX=;=f3L7psS2_XqB`Y63}KX|_r;9klnCx<5gYMjlspBK zywRwIcVOMMCjE1Tc{Rq~9vfgNSgmecNYbBJ8P=8W$_opD#8WB&<}>vRjVWSUx!@hv z$@WjQpJ^?QJ?k+F?NmiM?119_!H@2UkwPQ_{Oc~)t(2kZP+lwt6%wLSU@{U0E`@=aQrSYi3h&%r%ZqacP&~CD%vg| zt!oc+eMUSkn;oWc+UIlrxM)_e+pS;h*v#t8&dfFOHxcEQJL^Aj1v&Rj^$oU=FQuze z#4}=1CmgUW6r1BNzmaL2`hA9(+{q~h~gTwtB8 zo_Mf%t^7h89JoA?uajhhZTjnkEQMcOon;92r^FIlXjxNzVI2?C-)3O)0nR;obJlG*4@eK`uq7il0s1v>~cZADYWil!#I`bxbP_sN+;AC`LtL%e4N zJyC!+cjgCmh~QeGrpd;y9Cdz^Do;DG-z>-WRp4Tl&jG_sW809?!4X9x0=)S$nY<)6 z76yLo%V~97rZ(0+T{1*N3I%+j$flXII5rBxf{!>+2QlD**XDI>ma7n%3ZhsO8Yo(L z@ruFz*G&3!Ax_gp--x15LS|ke2_g*%7W}_E9`%x9jOq1%DiD4QCv@ymY|}xQ7vNsx z+`h*=Z?pR@>t_gEv2NL=tgG76Y0RS#+PRl{OS?c4=(dI9Ut-Vl+$> z%;lJ&9BGTl69$pFx0K%%Pf+U8usq_oNWR!8yJq1j ztP4!nq8fsL=x_ao)0nsf!_^C>(`MRMt$*qcLq%h3#hQwU^k4dk^waC!&OIRg6vYM2 zl@&%yQxI7-9R*TCaSbfFNt#{5`&MKy9^W#x$c8cqA;uTp2#f076Q|sK7Jo@MlQLnn zC_mtl&1gGTt;9SgEL4+mRQ#dyo!tiB?Z;K3^(o>&ZOc0b4hK-AqZ=|uMzg!w+A_nX z2rwP~s5Ef@%7JbfU)XDFeLb!j*r1qCP}S_YTQ8dqmVt<)e(atWRv(89xkBrdG&S;g zn@HvDlVW;I*J7@LZ+ld^pDHuEa`|`*9Lv-7Ke5ojpGr`vJH)bWVm`ITI{PY=X*!ZQ zmR(f8zA=*G-}PPL*&f{m5t0(KSv*#rr%Lklx5_~AJMV0Lb&;Ug6$d)peo`QMlW1f~ zEVDey?ZDA8z2sr|=pQqyR-Ha3V71=?Dkgg+!#dc}?<91w=vP-aqDYk$aW2Mmh{;a+ z1aqsy;E3eUxi3PiJr>D}-#LRUuQA6u)+u7Y$)?l#Yu%@<%JDo!d;1Kw8AM`#BV0TC zTwL$Y6ZTZvp6NInvQ^1(d{$6Y^T}<>G8OM!M}FMy;rWJWS3?WSK!X-Srv0bguHw3e zAO3IEBAhDq^i%P`@-FvWbNXOg9J3X;d{q>(;~AVub4B6qYzDN+PL2mxE5`6>_Q#@2 zb`1XtK=Y~b;T4T$s7W=Zts{vG*3WLC{6h)-9M>P(M|9&5z`&N;d%Xj&Iv5Uqq)JM1 zR7}Gqm7On^i-ZUq{_#=K>kD<;Lp`PJy*q_<6URyOmBjIZV?*bhn#RyEe&w>HH@e_~vLTHHThS`5{!P%G$#fC0g#9M?tFlE|R#|D#TnJ{n9)UE}72Xp;X z5#C6p;zzltGEFFu*nl4wMod6)>t^*%vfxlE3e9E0P|=S?fVjAel~Me?nMSZEsex0HFBXyV%BDqQ8WR~F8F7B zFLs|0e`+$0N~XSz<HSE1e5Q95F_SId@hhgCMa zYA)N7d2jTK=WIcfQ-Cb1u_n7O%*!x)$K1jEQm<>+oL?gL0hZ;%ab(vaZrd!n3A`j7 z=G^!USw&Ad@PtG9+OiAPEAre>WP$4`ko|1i?<1WTqiWzGZ7@Gp4z>HAZ*JI?(m5EOvpZQ-}mpo$2p ze;5Bx*4`zk#18}}m`~;Zk0$WnV1j=&f&V)p2F(ANP3te=_W!U6to@*gxrimBlLrkc z?|z}>-fVl+?s7Ea5f`lhc_dRNAs5Bu#1a1J?2OEm|iJatb0U0=u<`n495@ZQ)1B&s{?|elc z0>X{(+eLA`_&~^ZMuF}ipG<2N{aN<0o(5Z`bbLHuF{W^>k#EgwS^Sx=LnmQ(x(2s2 z$w>ykPaP$rAL??>%B2+H$!tSjU@G(k2@?6+P=gWi#$*at=H`~VC9%+N<6OiQ6$L~0 z`yqhoLg?ZHD!0M1X1+@=E-7YnJD zdU|?hW@eue1E_+bp)aL$QS>=|bi|4Ldf^{?M17^(33#Q5mcR&ykN_)7OMQJx)+QEg z&DcdBj!TJP(mY=rTT}tKYJeP!C+d6tq5SReclQLAs9rRh0H6e525x&-h38G<_4PsN@qpnF9|x=r zQ{TnW+0FsNgrRh9b#+jj^3%=L)qRpwJ1^h*x>jbU8YJDgJlM1QF97mYdF?BqU`)^A zJO?v7#U*m|M5XW7yZgAO=cgAKNJwaCxISbYBqS&}7!nHV z@nxi~FzdW>0t0}cXejLUO@JP;YxW03LC@{MPgzBj!#lZ0ER}|Pz{QulE2!Jp>dVlQ z!^4y%PPMjAkpy8;QDM>Hk+29z$ncl(c3{MARf=JI7XM*ZZe8 z24>d!B#7T)o?@sQoZOl)jSEUdkQKl?_;MTL}91EPum&$hJ;3SRGDK6CFQ zv$FL*$E<7}6Hwpya$M|hZm&?_qar0GI#S{i<5lI96f{)i)zy`?waGt~SC*CiqGq7| z@^x%-eE6GmE+7zcsNg(sb8JcOqKAu~#>cVq=f0^Su_AF)m5rHDGu3VP_OzWOHjBMI zLb&uOZMmhz$ua(i8#?_XN9(+`ZC*`HZb#+0y^OM`x}^R_Asr+MJ2UH0@^&LD3-c6T zmBF+8OYwKor@6<0eHCXk)f|+tP|0tSlN0oGU&?=#R@nToXtw`wl2^313&SpKY%DCS zxYrjp*W@CjQqzx!Lw)WS4PkmkX2&YR@$r049P>_koz=RI`bKX>F+mi_&wgdFU+8b( zTV?s9d1hr}dfBPHwN>1@-Gce)&ZDKu#Yy|b%+ku-qNJjdntp8byKI>0<9f#FFQ)g~ ziEQBq+6r&>#|gRXyOP_p%=Ngv4@!Y#xZe8J$3px#o$KqY&70Rtnh*Y0X_!oxh=1@Y-J#6 zA-XcF!!LJxdb*~@ZYdBD`HIuZ#LdjQw7I&z>|Nj7QB+&tdnq3D?BT)5qxUjcNZfm| zcML*(S!e0Ym>0X2q5nZ3_Y9UhUGmztbDh>=0=@}kwKw!Mu&{XEM_i__$9LG>zn$BA zTsoW*&~-7@ls2?7HW&QvZm%U$T{SlzCk=E09mSghIUeL+)NW%v#W_lKZH=^~OswVI zWW1y;OXd?8=^$f)&4qauMrL*{K9*M7mx-O3_101UnpWwE2p6n< z6Vsvr@sQ_M9*uMhTIuUrgiz>=Vw0srRV3Bm6hY`_5RhHIu5GAU_X$>?w|0Twa7iSg6x*f zwIzY%vg|a_W4gD$tOLluqlRmsBg92O?VG)rj-9cUgQe=l)M4NFa!`LOt>pv+K9K+y*O!;qS3SOBd;pxQ>uwEid(#X1&+ykkC&TLE zUHF;o^dub2osi?=<*M@hLJWRr;N~;(SFN)H4Ebx1;DI6&n@y-^aRAER%L_x0;m08h zxcM=IU$@@d6Z@Z1!=~RW*$aR`mM@fZy5X5sg{7!T{Om8$gm2-)A;yzp{-Qw0`fSya zpD_cvY8n)`Jgt*pZ(Cbi^?IjUTl1sI3|SYvUe}lJ&)1pFM>V-w(Mc`gJ*NpxmjYwn z3$HXii_>gIqeL1&O0K$`Ur7``?#_09h;lbQuF!@y+uRUdl;6ax+q`b}@4Cz14q9ci zd6(2$TNzoxZX4N^61AivVnl9QhtjjfKw-LSlDt*hx4Qa3Xw>Dea4_g+$4 zmKOS#vwORmodxg#Hd_O8rY)40Xf#g7o@FrzdDFQ9P5+x9>t~vit0Y8h*IuX zdosV*-wjCDA++8GS}%@v4<;q!zb@T=Hr`KO)IK`JH?9Y5!BK$dr$7S%AYs5jtN*Lw zvr8j!%`q|Yv9;x|ufxp}_{4Z`YEO?rAjrqHS{noX2&Wh%?=(;CH{Z^t7QYqogZx`R z9Y9k|%jL_(5U7>o(7WvYPf+UBc2@L0-qWE=LnC`{&*Yg$LN^CveYKNg{!3=Uubz?M zg%b#lqJydDmzUHJnL+`G@bq4+lFBXDCw{toC-TVLaOu+Rr^yYdsJALMQ#8wK5(MAc#PE*ucmpCEn9xMaB{wcnkM|%GRL;=ks z{CXvLeV?Fp`s0QhhS<@!0{-Ax*6VWLHSWEJ`l$Bs4z3G0{PP5a_i=EtLq_N%;J@el z5C-#-VLxGxb=w(b8HDI5m~(Iif_g_f^*E-nofKc~=!EDHm~wao^0A; zWrm1*v5gb*^`bXgGiss#SXdBP2zZT$cR^YIrmCcezxX)-nUc0W~o46pl+`pz`Sck8M- z)}i)#T})tb5s@#O@w(GPwRsTgPb{CeeV>ijLaUJU7xr0>Y+UaxqTb%cN zYvTvHtViE$)}*Ka?Mhtg<{`ad7WcIjV@o5=heFt5~FViU; zrK$hmW#L;FT;~V+zx~{^hd%4p`#b|3bkUSQA**Zg7SgtURh+D$ zeCoyJ0p^j<^L`m+?OvSBZ?wB{h_yzK;x$R)Q;2(UJ43a03)T86{gvZfn0<$Vv-uG; zY~$Ej%+Vt2o(^2su;b`p2*5-OOuzl!SQ1R`WF;dea0 zcW|TrRK~G1wz=PYafnER=n7E90M@85J&K1~VvNz3 zp)N8$MmE4IB@$Sn?w&30xp{05Q&e`}%|En>gJlIwnngI{SWTZ6WHf|c-G9<7mijH) zrwmssR?UuTt}I0UP03i zxj;!qOEE)J&CXOi5r6+0S*9qc4qZDC&Ye|f8N}Ioh)eYnh`-7YhU=Q(-3znkI#dVK z)8q8%J__Z|x2pOx6@8Xn{YvS;vJ<>Wg@_tVl8)GXY8*;Wi=`KN{3#e{H}l{H#^wmO zDx)^4SXvQ8+@}UTL+RNiqsutCNkBf(P_{!LHxE66qn`;MX%$%+YsV_B)#e$1tGj9J z*;AocA#^wEhC+R!cMLP`CeYyta05eaIPXfAPPNLAoZ%7V?{5P1H#gqFMzZac(IoR25&Kd}EIlCxzVdFbYKWi6M&IhM zms9U`>bgM6u&K2q*07;@4$Oz4Jn|X&Mb^VdIz?7ruurooJP@}*D<1I}P-8;tQn_76 zH%aMsEP03xi=XA+@uV>%|6xh9uyxwG@tSYqZtmrWGSpVlhcf&^l$~093-oSzdHq8> zHCL9MdykH?;a`e%=+ZS0o2OIeWvjgKNrojZq5Qj&G~}6H)z%SSemvug#fQfT#kcC} zo_R+hOC>)6JH}=~}Z4tw-Ab%$tEH`Klx!*0xj%CmVZ<@JgAtEo#K_1L$re^qINhwBy}TI9`~+e zo3N>&rlG#M<_;O|EGE-Q`-wd1wV1@r$2NOv5>=QB_Sup)y!t2%5nQ>@^ZJXvnd!Is zoT{z#>=gHp7khZ@dqd^>_SF|N7T-J~$hVl#TwQ^CNO-8616|#AgxmQ10KUaaR;A>9 zQ-gV`VEcwYeK?|>hW{PX8fz15@At;i0 zf`I$W?En}}&2;>eZyA8Y5cgJ+?+WscaJR1_@-hVDB1%#i>wn*2z@r(?X>D0Y1C8(c zZZf;dJwSFS^`!pR(J6{$Un}_Dp+t|9PgLqHEI=MU!NXOJ<1(phX2qp7(|Xy8s&H1g zcvBK(GEF#iYJq3T^uTT67A7VyOY3c9GJsu@@=zUY_eaRFN>XyNL`qPCIkZRXiso_o zD7?dr*<~Cn%=)?A3I>%7Kkq*2hto-Hut44Hk@Ri(i2@OH*U>FcImXf`FvB~RG-sKC z9^qomFgCf-BH1GH#y4ebQSya$WPIxs)y(FAUaOSf8{oBARo^hTn1Fp?Ttq2%(26e1 zV&tYY{W*VGd>+LdPYUSd1FKZZY!5F*!@$8PTKoQC1LN=R#w!}lf-_zl+JlE{k$?MH z{$`=5%{FDK{VftM}s<16QGYKnHC8DeH3K%(V z3>1Edz|Q9LBpC9#EgLSI&QD6(356z%Y;wRp#*<2@31}oZA<59I<_<2|Rn*lP?2Lcqg*}Y&V=BU@;*^XDSRo*#(3f+Rgt)EhVU^{(lW3Rb0+HJ zq%E^JKK=j$G&M+u;4GEvx`Sv{H8j%FbNbwxi)z~ns6l9bYUl>OL>iIN8CVnp{8R(3 zbjsv@#rxuS!YSdiWNhSY-?vZ@qe8<%kgc4p`N`yT)Rh$-nj4T+s=UcLEG!uol~jd7 z>YEs2RP&Hf#Li2vQqjJ^p*jft{+L0~#Jwm-RB#r;W;M*O%%H96lBK}Cx!bV}=_!)0 z!5B8`Zf>Gbx32S6n3#Y57#kN-P>2RSxpArEW+2*_i3;DjvFy?1^fTf2;0X{7LZz-J3-k|EHem^Nk>BE^6EdbYOP{ z7gk+RP+(M#pW#ajcV%Zcl@kMtf`q&Wxs*wOariW&`n7u;3FrtMwiON_e2f|=5k1?2 zMRI4bNzL{l5&DFMii(SgIZh|`;p0Um#Kc8KCBniZz}UJtA-{8U5k0p0H75Tw)PA4) z-d4NkaHP6b{LH~#UtOyYX8VxfKKjYuw<{-SD>haxS4~2~)>cj~r?azaO#JxI_WllC zO=EonBMZ~a;-BekN+^@U%*iURrI*X6#pWDSJtKnwa1uLvJBNqM+n??)Lq;7P-rn!v zOhBM+>bLz}`nzjK7Y8Tj&zDL2MFYVfz#82Hult{2)db**F2v2n8kDw|Fu5XR*UG|# z;LhIZjug}7?d|pTPx!vJwyl_uR{R`vXC8cT8=;_>RM|uj#o_X;@Dn*G#b*Tv;I0Lr z`vQKCj3!QHYHEs&t*WZZA&5g@=O_LbYz-i+)>LP_r1m6azj3>zN#v*X`Q}3eL=qtU z3ZSG0`-{-xYx3}Tc)-Af`HppWZ%j-~U`^4(wbsOeL44q%u?O!!V42c$_LeD97fe^z z)X>u0+!z5H|RuR%_E zaR?(pnqS)K6htt-r0LX`E2VoJH_vS!CU<_2CqNC31e_ZD%;8|-hy)%sz!&lTkmO*f z1uMtW!jslj6wDS(03`=n4iY1vPD2A00s&H69w4^y$r>kMf+~yMUFm@)##6}4>@no_ zq6XFgb_yrw6A3IHECe>JvnbdriO&|6o6G)~xymgNbZfPxj)Q@^C2V_qYcAp!1EDR$ zD5lfRH8ML1yjkkxzX`_^05stV;FCcALO#SZYRY(yatL~X1NmkYzTuaBtja;7`miOK zocXkHgRaGDRXZ?TuZe^p)6;8^N#{=n$S32#2daa@wS&%G*>iaC*C!WWOrPR^@sj*4 z0G-0Vq8OdAi~o~v>EAM`{;O~4zq8AY>mw7=|KVf$ANMT1fP63_gS*UN|F44_IV7Ng zMwam(W&Fo2x*Y$$z3#shLi&H@wfvv;p8anb0bHa2fJPq2pMMnTFAd1Q3>(LPhezu_ zr4nTR|9#j16+!@j;FIp%KMMDc1g#wZzCGc;G+s} zxzHf_Yjl5`X+^XUc#2E!Lz4gfCzMh$?<0sOyyN&a$)3X>i=FbI=ISYnr&o{osQ zF?b{Z&&u`im~27ue!NYbKMe0!6pV7vwAkpa=)rifYefw;=b}l(f9?$aS;T?SGU;d zGyM&!MLvL+DxZdr`m6T!UDo81ggI-K_9Xw)5X$ta9Fmjlk?8#~5&ZsUMbp+-_|4JM zjy~C`&rsY7y}SeDG*g_?0|^nw_E3I4p45o1;U*y#B_2g)8J`2Z`4|m*3BF391qQqV zWRwN?ro-^Ine6nMZZL4WgO(8KcEatLW5Gqro8`AT1;zTIHen#({Bl*`t3eo1B!*c^ z>|Q&1v25%uk^PH{s$2k89;M!p0-et;S>)u+asgOhhwj3_ISwzg>Vt}oKb{Sm5#E;| z-`z4|-tOUJ$Q*)tS$=qEbQ!xab1s11%;hEE8Vky93+5Gsp8X6KQXJRi-$FV`3qOnazPx{4^Ufy7Z#(O0M18dkI!3Ick zlb>TpJxRhi^4fD&Q(aX1VHlg~n~-`vD+Pr7i8f-6-ou@M!u&yTu{Y*#HG+wNI~@|X z#g{h>e_IHR6zWA;iOMj8Pz*zX8)Y6GppSq?E<`6z<$d)HA=^RqHQGen;!UEkHu%3F4W3#Cgj%9VBu+brphD&D2X90?~LFsNF9D?{2#Th`*I-xs8PWNWoc3(2Ux+VR%MIt+~!jn3eTD%O;YILp#Ym1mGAQ~(ZFB#xm8#9bSLigtF!Bkh z&7&;geVh)KjaaWrU(|-g9>r3~K%wvI}Ei)1iM03))UaI`9>*Qg}yHhhf59MNbO4S-v~=&SGOrMh|Bl9&dU2Y%UUKE6?1PicH5q{yg@p5_W7k><~CpCVy#ps>@I;X9`M!1*CQ zZ0EP>qR#6N(MdqY_7oj~nNCKFLIPD{&9^ z6?%^}YP?`3-=r7y5 zTe0La+3w*?n@?+6Nd>6inIJ0-(h>Cv)^;<-JuQrpwJq7tZO0=ex+&rjb!vZ{|BT9C z){J!ApEZ9gKeI+5@1XqXUY>&S0nHM=DbZkAFG?<`V0^(o99X|Uxs`soijszyR>Km_ zHPNL;>aSVA3NIQIS8-!U(`cqIt)}6ANR?S|YPjkC@(b$+wBzG_(?{Sqv@|G>c2L2; z*z#NOSR`kmRi})NABKPYhFkPqX}^ud?ixDX0^nqMFW7=bb~A>Hf3e4tU?l4|i@^aH zG6EKYTM@;^z>_$@BiY_`?r+MwFX=1aWMA5T^%?4@`3lND)hXWL;=3i~J)-v)g~}(b zqwo@Q7I`!993#X}qD4v~A-&*0>?r#A)xP-hw;2z?i@h4xC5;(Yl-^a17n#+$scRV>Nry%#(K32_pAW%P3OjzHH4_H>F#gWIE|8qR8EV-ar9mAyPUX4P- zDCbN(=F^De0v0)jEa%)Q^KgzNUh%ou4-&%o;rz{I6_DH=$_6)b-uPv+dHb)ZU*UNK zv2B8WSeLt0&aNUcbO|iKoq2*zpmb9_KU!Np6Yy3I{&92ntIMh_CSt<64Z5pRFC6qi zIL6<~<`WmAJ&-<%tLEJ}Ef@ViI>8q33&|BJa+nfwKFuj1crPCjxzc_U)V zSM@P29;VUmx3-l~A7Y5VTr|8dbcNDaCW(WRBliZl-!;Qm+g$RuSBj}yH>vL>iv9{I ze!VO_pz(J|g_VeN;-MIsc?6cd04N)*eId2IoxKx; z%%lCcI^?(8OH$DaNaKeP%T1?{QN6yjkMqiQm))&OXyI6)sQ3r$o<_v zs-bW?3M3fK;#(;mUh9->pMFAJbjrRIV_X8+CJQcJ0>BaJBkheHt7MbAs3U(*&o_Rz z;bE z>8Ksq)z-vh>=>!_cp2v}J~=p`G7JtzEXan~W=e;kzB9^xW}V_h;Ka%9s3ygS6w`+` z9}rNQk}D>RFGb7ldEoX2P5x%pfM$e7>VA)Na z_sq1E_JPMnWqj1>1Vl0$_00rQG7zlOZb`!(2(d(xqwe;~NDjq{h>jhq;Pw;6PV*sg z3Ahy53xbh%Ev0&@G>1=6{Pe`bk%WE-GXy|AGU;`U9>Fq}r55OCmc}bs8`%z}_<}J* z>*W#{@DiQt(N(Gd8?MXn5WhBk>yG`Vf>mMsz3H&1u}Q^-F678 zv=b03CQ<55v&~fu+c@H_bM07SjOWDpqh)~Fi}lMo)@95rUmJxmsu2;7HJcv!!N#Y0 z#5y8WA3td<=rR1~d_`&jKv)>MxW-X#LBTh-E=-4Ar&T3qPbh=>Jj2>RuFDKu80!}i zvlJsdD!+3Z z57ZSpD8YQ0(}jpQN`(UB{X%_a8VR=1Oxj6|MijP=-!s)82y$OD_=V~?*oMf1ZXQ{s z@^};@!6QMgrHZ%<=KZ#xkkO!7Wx$#(vnqqvOL~OdEN`M@fDx$$OnPI&!8zNWpK#W~ zblJ#pkY1ueh~r=T;(iHr>rIAsBHPu71V5%R#49F$7Uti^HU^VX&Y@y+71^(C^W(~U z5)rz}(_d9|8GCB2?JBI0X~@NI9yAR8reQ`)*`P$~$5D>wIOT_UC{BzqcDTs(DV$rVL&Q1HiyCb`ze6zkZSbKxqmZnwm0sKEY8{w%~VFpF_0 z92FM-TmO!aHj8j4PwV%!qJXEG?4NY%IO3et8&{5(u5F?_vLFQQSb{wRA?RfdQgZix z$qV>um^)k=5AK65UNQ%nZ3KG;`cK9f&ZcQmID&oNs~yPGjf zuu5|gm54ctp8sss%pS{K>_vxT5$YrePCx=9)0O)669dHMJp#|GiFD!<-p}>bK41Fk z@~Mvj^J15}t79}hqnR^J|L|82G`xqu_Lahn#9#BTi9fSsyZ6hQYgPoe35Jj1-rKep zER+S^L_fkPOIdZCSK#Mo)U+}IRf~egy+|*~kO>$n-T65qsZw`g2Sli~cq+dkU6%mk z7N%ef9Ala-oo}5w4n@vHZqzU~)zX!|M|T#U@n=!!nt@oHnGv9e{AQv_mum?1jP zOjPx~u-E4h#;V%KUN`~&RlD~8n7{0pkHom7bDaNV{{LHw*MCKF{+;IazXWzLbN)XF z91|PY|2W!_`JkFGj26t1Dwy#3r^5sn%rMsh0YyHfpgr2B&*ET$80b0FJ1s4(2k({- z73Vg9UdVM1m(FFDy8!)L#(rUCCm%j-o43n=5hcif`gA0A#faa>GcU(2Za%-!T!WUe zL4gC4BL>;7;~glX9r{7GA&B><|d+dWnv;<;j4rqtzAUA0+gLfJt6aQMGgEoA8&&m$J&rBItu| z7~2T|@^v`)LF*DDVoS2Ih?lf%Qq<6|Av_HIdSLPE}86;MLS0^+_71RR@9->!V)Yz2|Q z5J~{y$gYP++U;cwOb|6`eS&Z0?(UDM$KanXeUZ0FY;o|VN^OK3&=9XBGV??gHPdqd zVSc%&V@pec>7|@eJf=T>AnGC^A>D$79s-a}h38X}(`^JCAfci5d3T_+e)~cxAqw>M zeSrIGYyPQ)B~dSC*4DwAQ2Djl{f!- z1TZA%{w+TIBS1W%>mxYa>emMY7#)4rVBgY^fbIo+&2&>}0=Gw!$`_-AJhSiqf?aZL zhol;jGUIe~6%}B88{Hp#S;NE2%gds3&@h>q8C&H6?Suez^%QbPL{v`h6}lfKE1n2= zBNRkT|BJo1jBTWOw>XnYCYdlZ^Msi*!3i@nb0*Bp9EX{inVFfB3FCyBnX`Vi_e#6E z?@IgsaHU;o>@RJ%+bX+V?W#J@Illz)WV&CcTA&yxeFlc6mq*`ltE{arF7WZQcGwXR zfH*XERSXOYQAlo0h~Z^KyZp+*z60C{Y^r`;x@p}m`!BFe;)<`~S-f_&b+ry4XzVTs zvh+3t1iHF>d_LFd+1b_A(b3Jt#oSjMoU3aZnrZ3&lW3VJW~dVs&{#A}rg9z&^ajFh zc{XO25?Z*K+8aDPUY>7nU|$0K{e=Vt6MF>({R8~JyuW*Sb?^@lj`qS^N&)Wf9VzqY zON93-*V$vGL#fG(#wY0Rjx%x@y*@_)CUR{Nbd{=82iSz^j0N=XN z|4YC+7q_&mq?L+( zCFM^Rv9zUaU?3z0eU6ao4brum_fnODPRB&JfnvIoLHc zGO=%)&67Qoy6k_%R!V+ns(3=i#DO$rm;K|rRTFvy)G8Ai025~!9dB>`eSO5Un!uPh zZ*LuI8D|rB4SNGmOC^rlYg?y=dh{8&(q0oa?-@C!OfG3*kzJ;6`krO3HrJ;o$ROc? z0gA#R(}O)URJ7!T#AH<^6_v0*RMb>s;}VkL(6%t8sQZTuY_Y($>hK#HTLT6)6$1|A*K-7bjSGs&CC)cBGtfT=VF329S}01a zm1kkHedUG5dpK!!Yc}@ghNh;lbzlM-P$srvV_jTY0Fkj1pj#dg)`od7H3L@6SDQ)t zi%j)R3LdZ^q*A{O9V|Lefs-?_QPNUZBWhWkq+7EzH#NGfU!0xAsWm@I?P#bTo<~J# zTG38Q!#M%X{e#b(Sd>t++x)vHX20~qsVba~iNkBw9S!auVS@nZ`*DSt@#(T4J*35^ zhZxLslUC==?QI?&uP=~#0eNgUwmIF^Kxw#sS7m%sq=u4qY*OlL8X=!72IMaXLoo12 z2r9S*z0{!KIB{eqhy=;1tPdh2ka$Ho1$pg1^XrSV3v=uY96UTsOwc$e&9M}q9UVZ*V9SUk?R=|E~No~@bIwkk4}#*ZOqH6>FB4%rNW}2;rMx1 z)KcVTW^PC+GM~l*jrzhPV`}XjZ{fd1NQjNoeXlIDvar59!^6bK!pY7^%Sz1*n2cB- z8D2>}E*%ZKp5+ek+Q747_k)m+$HhT9JKZ)gH4e_1?I`dT(Bblfl_ZCg##6#VXX-_= zqf~z;1DX3hyTHz3?_yx@ZtbmaXAV@`u`Jzyd4>AcChqp0j=N*+OnO!a}MlrdqIw~=l*aTrYxe|sY#UurDvV2}2j$Usr#6r|_##TRsXr!ht zx_XhWOF4t~7a}L|Ho*D~ABzsrlP{}*W&%87f% zq=SLEzxCXheh>FmMlFb&Kox!Qe8|9-Bxkm~;dvLzk7tUJyq|1tdkMLenVO*A&|Od_n1GA%>@vRZlTT8EJdpFlArCIldIZN%f)v_`ygRp6;XMAB7Ei0sW zQrA6e7z&Lr5|>ySogUz#rKf0Us(FF$^|p6hZ#u2pA2+o=orJlJ&#w?yC2FaAmZ1TL+z` z)!YtAfVY8byEP6CTRmb<9&{+p$|Q19iZG6{I3)-+d}94KmEX#W7ZepRywN7H7ao4I z!~6YYf9ke4nxLwhMoJa{D>sv{Z%XXq@am` z`}GIdm-@#O)*M{?u7Gzc3lfAsc=%mET#pRXGEK5Se z(aI>Wv!F6Ju;hZIt1WYRJO%wEGulgD+O)jH!cNc7)LHX2RTjMIID=OiKRiiOrwP>*JtT z98FoBv#u{Gx_>?&1kBHxAG^CeJV02c@of}l0C2>aWsNJgwU1WqT+$ucNT>JCUB_Z2oYLt^TM0O{P=%i=>s zrz&J9rtPzk`PpMM0WYD2+Kgod-IUb&aeJhoVkjP*1jB>-(DK(`S8$WpyFJA|2AX;r z4bQ&=y>zrRtj#AQAF}k5q@uGFMmoS!{(PnL310)JM`A@~X`gN)r~+PYqu?Ez=2g*+ z$!B$<(J5%w`hpJ>@rQj3HT7hMa3k0>(@H>G?))cqP9Wl-lp$Kd8FobMf4B2}@XvSf z{Va5=?QHB4G-_MLE(4yl{C+bGOLGffIJiG2c=bQN$yhy&OzGzoBh~tW7_LPC!yI4PVl7(X8hhLWWJ)RLtl}1|WO7 zlpLxkIziE{5eng?QLkE-QzXN#9j$O(pGye{dr2Pld_OnAOa-H2rWgrLfN3Su?;m-S zAj34O$;To{ih&xAN1<>?W$O)(=@p5N6`!D~s3;arAJJpKy}2(o`Z=+W8OeVzNquI< zk-GVS!&=B$S_=R)mLfH|hsW1C3~fuzLoul}bQ6+TGsKlV2f`*-fx2z4I&NzCHqOh6`3RVg!0cU1%bZl%Lg1_goUQ9YH8oUkA z^F$ZTRv3G^@*T-2j)sqdABnFI=Z6T%q!^jG1~A1wNAn-}#;d0d+L?+vSh?wF@VwGo zJcVjGvz3l`x~C0)7KLM#{)(qc?*ghL(rCo^8K7jk1b zCvu%n#Gp$i)U4C}3PG}wz-xEv^i18XGZ+=@_nZOScr9k#3H_t!kL_uq^^yZqix6Ea zrL@ZkUppnOH4E+p6jL1c-p`_+_KIoEFjS6;8b{n9Sa*0eYxhUoG3!-64o}DD?$>ol zEx5*Wv*$1IUXXEX_u=<=3``ZOUpH1eRD+z1ETQqRx--wXdtN2g=|5MJqg!l&dyNk% ze}%VmliropM8TdIi;4!n>7ZFGi6}C)xt4TO62ZMon;D8?Oz%)RU3a`_U?8|%Pup`m zUr?l6@%_F}m2Gzg_V@De*>UgrKj2IiiQ0<}D8G#OPQru~?bRb^o}IF}x^4OUtI#to zcc1&vg7iQZbMZl5^o`2& zjJ9**(dg9pdw|RQ3z<0v>dW=dcbvw7+^`UB5lzjs&XA_PgO~6=sqaEhZd^jlLb28Y zcfYcW;T44xe|e}@gDmR1<1NibVQ0k1IF^OTNTigw*&c3@VM|dOML{#*9kVVaV&pus z4J6P1oY60%Z0=8p)%V9zsnF#PuFB87FZT(Ao`leldVD~Lk*&~9=kgY|F{2h$ydiq& za?Z99`qc9dr&g?5gXNzIxMA4rJC&3Yb5KxFK~nJUNJ)&;NVrkW!d(L>f1#t!BNXWB4W!~@ zIDEk(*-XO44?)&in6Yn1@EX0PKDwb`d9D7#Kol!qZ%q*;rKVA+N+4eG0`WC95;JCEtvjWERLlE0E zQd%KL^VTXO7mUAJW`|f?iGwEP9|R&GnAznaO9ch9FdM`%!yvNI;&le(@6G@Mexs50 zG>L;rah_4`%@Tq%b+vUcfT?GFRB5uGP(?V*iHU>92^V^j#>(exbud>kg==fy@|KqWiRQ0CPuiw6C%c`5%t!6t-?1)#vxDT z^tCinGP5wjQlgSX;^rhJ^{7!~$rG^19mtf?cOaWAnO8XC+*zAVPRzbxBkLQPL0kZ& zr6wn6LEyouREgv%?KQPKeSD0K!v-kDTj~k)(*Ch-5}8ZfV^GJ(NPTCngJu}H5+sQ& zF2DxljE>M6m{~vGApmvDR8C}8<0ev#-4EJ-e8w`e8zi+$n{-E8z}XLfQ+x<4N^#%- zbmP?s4RedG>`RZ<*W`w#R9@qMOsP7IGwWt#ZhCROnZ69bLeD$cl?t^;H%R(o@=F0C zSJ#^A!-q-}Gm2#Ny#lf!el9Gk^CVKUu{d#jEUMlF8MzBmX~i-YKg4e`lM~cb-8#dh zg&wbvA(w!tO+1QpMBsePWjH6Dhh;`I?>9TP1(hVQDm~tyKY*y&3Zd0X06ZCcr$91) zn;*tVY%am-fsiUvLMZR|EUYxvXuXh1TYY;oFnYku4AWV3U4#xEPViTI@u@R#SZ%m* ze-r1QaksZTBIm;+759@n2sSWYyirjEl$Em--R3mIXEB2MuHU3buC8tU`f@0z$yAKZs=^zZ=s*0v6rz-~ zuyl%J+zMf0%Vzs-{fcG04x=SQu=nu>K6<;Gr>elnk>5Wx1oq z&=SlU@qv=+gn1>{7Ub5#qM`y)Fz6v~{S#HSZ*cE@deq!$Awxw+;^c$+%p}~Y{K2&A zmcIDXTJxP2dX^%)Gs`I)pFKGblA{zgH7zZzjug0$-tm;9lJ9`<{sImkKJ4HF zqv$YQWCl@^?!90DshL>|8jeV5xv4LujAuhmdolau3=A|p99(-aqq`AO9_s~x^fb(% zr@+MY?e)!8|5w@xsi$>eShpY8C=~QYt}`<^GQq*fCuX>X`6xy5?*38qU26uT+ zvsS!OpXJ)cO80K|bXaLuJJ=Cl=vGyO9k5MyvS3AmVee+|hi<`5fSYT3& z6?1dr<2bim!Zw?mI+zV7y@!rm%PZdkxELdm2a|;XAR^u)H6ault@0MjAm*^A^TxZ{ zjIfxtW=FJFdVS^8CLQ6MT8zsJgIM)8cxT@tg0SEi2Ddb6*@{p~?`ZYZkjPnz&4U|^ zl$avH*+6k&C7N<6M;{*G;aM=%)*%Saw`nFNo9YN@`uO#DX;LR+i$IH@6Lj_4haWux zgHLK0bkb#)indqL(UX!?Fv%)WO@g3)qxZp@2Syl$vM?QkV+PrWZO#0i%nLS7=z(xJ zxqsq!mNH}yN@ZlmD7SkYkw?LvupTL{`*qv9%W{Q&=xVuF`Bn*wfcZdO-X|!c|I)H5 zd}hVc*Z>L{zhbV}>E}n3t}g3vI%pY;sT3`Kk4nuFg{Ky%e?N?}(hUV0jV-=RSV&h= zs;Gd3XN9)|Hb>GWgOstg%ESArbhJ52#UtBLy}9O#l9wZ+NXXJ(B z|3d7}Zp>hh={+a=n5+v<;}Z)rO-W=g=inP6jL->*wJ9?{)VbH)=fs~o`0ZiCoK&1* z^zuoNr~0UTXFPtEn=;7GdsLvtqk?NQRQzTG|Nu9IFEW6N8a=FY4WkNJ29OF;$`wk`#kq7$|>;LkEwCNU* zD<*9-cLHHks>~;1_L~0zmfQ6pxvgB{@wqmYRhrKJ(WD*m>0{X1CeL+3ux`5v!*_X6 zrQM1!mF{E+^I0g5d`^dRGxkPdG}^p)i=ev@W-8y_-hMCJRr>j0gM6ginU%k|a4&oJ z7J%k74t;zPjqQv&h4_}<_%?Q8ZtvxuW|Y2@g!a)ww4b;b`#~yT(Qhe=*b)78@yH2F zKy?ZlyOS@fz0oJlDBpEZRm{hEcaFl?#N6=Pg=b#A^*M#h0G}C~$sy`s&|P zy{V)Buv#JFuucnZb&n;fQjH59CKZ1zFlt4uR&7brP(rJMZU!a!+k2n~#QU2gUVUb> z@!sSIe=0+LQ_M)2J28bjCRmJs6cO!-s>q+aHGGFe^c@g9Ok7-CL0I@58&g=BQo9a; zK95dtjOKwI5osoN}rpmKB(*7sA#;Q9Rln$l2pe{H2`5b`D2fr|ns!`-PMe-vO^lGc&s-tLMgNjDGovTp!Q?J04o$maRiaF|u1)I~&_FY+$--@j%$Wv#QZ_5LHZ3 zCkJ`znJY&_M;JcX_m&aO84uyu1N>NqnJ)W35wNCY3Wjy-TR684TaL6+(x-gc_`sHT z`V2}NNaZ!a8;sdR@(aO)7b*2$Bsh<-IFID5$q{y>($I&4vzHv1dw#dyob!jLHo~rx zO1Q-}d};6KsB3F$YYHZy9+ynQ5|I=I9zNLMNUb?k=f0oxHFY2!1xpQ9Pf9Z@C=5gz zWOvR?XRci>xWLITJ#(ygZN~s==bsq+bYz@UU+0uqHx+jGw)?FEf8cjZeFwn3SMk*f z9OhJ$(z7y951Z}3dC#@e85~UJ8{Tj=fNf!Laqeo7(?GwjJjaVXudI{~Q3ueWLkB$* z`kixf))>>ACW*@KB}u{zKC7$2Zq){Yl=nYu)#>ZqNU-iA)?LDENar}okAFAL-B?~< z1*XftwoO01IN7*}rlz`Lb}CUNq6d6^r~y|>LIR%ai%r%1X2>5#u2yIRL9JNxENN=K zwRBz|6{2&}s0~XxTkslM^Y;4s=EhcD4jzydkoz~eKU-nV0H8DJBX4aFcM>@X3g%hFQ7t?lg0o$xjE>L{Zn)f8s8kJdr=NWp)#viU7U2P3&sCual!4R+AW-u>KT4O#&>y9@S25$;AAp?Pd^^R=arkQR!A!Bu6V^=m=1CS{H1C>b%!YZx(c z;jm~rIJAc@_Kl3wT)aU$+Z(~wwjvU|X?$yop+dHL0nge~J9~ zwiDOTxVAA^2k-lZN3t-xnR)YZvAN%_&CU;tyvY*BNrhwhl) zq2_2J4!zc6P)Et(`RGo{Huv-PvXqARHo{Qf%fwf`?TP=DuS`(C)`v+Bi(4d|(1403#u;I|lQ|&g|XahaEF22T~ z$dCdSP3PGr+^UB{sZO8v)|Ur)uo2S5K6E1nNZ6@BCV?7&ex)qj*RNaSR8$(Ui||tg zLyqFV4Wl4%)Vis}UU$@l#@wie?RC~rJ>(~s{9#rp!iGiDEW>8U6;^OZ+@)FRvAI;3E=^+GtiWj-|SceLw@Joas_(jvpJ>mtj_5<=9d zuyU)%bVEX2_-mJ2Q6KQCw+@SvJ~lxF%=YfTYirK*owC{Pb}(s=){?4{G<&%;W>ni) zlpH_~zQgTd_;K#=UnA2kc>f@3cj2>OU(%R?r^ivFsKEabY;ESx_9t@UBtppa3TVkn zsCi0zFy{PZ>FD->srgkSwXkDLsHXl)`qbj`w|4o1?PvT~D{R+aUX2Y5?D;53oC9%} zu#;EbCxkwe+akkv)jv8;VxF-m*!R}tiz~W$Qs+2_RWB#NHzqaZG|2QEwsydEXxMF4B6jWWnFMCSe4OZkN0;p?cslu- zhypy@v>%N7^W>RHvI5<~s&6N-r=QK!5~BMgt8i}kP{Y-F#yXE%&z=EpP8RN zW5g=FKu)SDuh5dtbjWI#%vQXt-X zC44VWr+7YA1y{#lE!I8aU8%{vucgG=n0l{|XvCSDXb;f{rj|-ySv`^Q>PgsmlKMSq ztBkYIZ;vhF0n47y=?PD`Kl$~XG3)LPvV zG!jo;9=IU=M#mHo^LTl+-c&ff%n##5`hOh6{-2}Tr);Jx0;}S{>3=N``}g3){{$5G z|J_tbmVeJl{O4H(X!`$mK?zJieEWY-XIR+(*QYa?hpMRRC__$vSbb~uSPF;&2J~Za zjY0fk0X0TRw)_H)315L}37}B^d!*rc-v!hr+Oi&XJ+j+2z>eqw!VWrqC=zgp@;TmL z@+v!>ZeDab9K}pIlH}lkZhSiu;b;HShBOoDYYpC@4-Rj$LJw)A$!_~}7|q`PV5;@W z3G#Xdu}u3;qtJ%wSogMWxG}D@7+uSZs)u28`J2AE`~ROJcJ>@`9`&mKQmZwq)SD0wP+2< zhTpSSf)=uW3^?iYUFZ}rAoz;iM8qHGaA{_y2ZTLungfd*qv20%3T!MAGU2eOqsY(@ zIfG(KYK{p0Xnk!hl6Ak_q_~BB>2qaFN9arp1q($gDFIIV4DAW@Kuov_hL?@R^!Yl+GhnG!2-WTZ<%w0wHq7`|N?#PORUH zM}BsqAjXn3#4fLJixv7hI61gHUI7dHk(YUX{NPy!Lg%!#G71$SJ-EZ*M4>2=YGUF7 z-pZOxDTsfP!d2$x1k8^PE^OeLQFMBFdU|@jzrDSEW#+iO57NAh*U`zkxTvC&>1jw* zi%%lmZx&D}u%}YUz)nxmRP&pO1^Lk-^YR2j!%q$an=l>#&f3buAV&^!_4s&u&3U{p zkP?;CP{+W|XmUM9{me?)3Wj87VPS1dOC-pSKyPz3`ek3B_@Cecq5i(UuZ=Y|Gcz$U zTZ#fV{8${FojmKI3I@(4~rUIK+*-B0)8Acf~wY|FR2K6>38;`0%D>Uh>?JP;ylA$l(;y>MDycRb@dg_ z){IP)9#>V)6?OI0WAm~3y@Eg>eJr#_&&{_Q8=2GGH-X0%aAvHZRHaa;@|6g>w`S6GcXc(BATYHB(rr(L9;^8evp)-W3+0BrOz}B3= zqbM+w#9x0W%a4T`gG2n`^|nv6EpHdjTxy>B53fv^tkBp;Nl8gXSsBJcSve66%_ku- z2^DSc5Xc-W%#t4v4hk80_u}CcLm`nE&G&$Y8qzH-BF37ewGtB+1cd&>+Z&$%!uIHB zt4qqNN`Ls|0=d{-T_1V;5URGW+n!d^PRhbSQ@enUwTXs=fu|I>KwKPK(gC?vgM#_O z{26YMWgs8{u;?5`cd^M204S4EGuz)lOO8>M{h9)z6SdkZ6tr928|C@i-S5LCoSqeg zyf&WGPJoWrzK#r7<2qtCvk@n}SpxVgG1 zG}M5armF3@6AMGd+eToedt>8yd1+@uQ-jtoYm1Juws|>Kn6Vvy2X*sNDDdOM{fjeS z^Ejw5!2EYoqn{;l029>GDxI#nm~VtdriW-rOoGaWbB&8l4-Zi|nKkMDSZ?jJvq1iy zdu6Ho;CD%w+qyYWEF|LFd;3B&RuZBW+)@ECXyryg6KW}_z&0Z^;Q>aX^5Q&e33j?> zwTt8C*~!Vph2K52$zEl9d-G1ucNnS-M4fPmg0Wmqxci+4PXGyWhUiTE`43yv) zd4tQnU@^GJaM7FtjAYd%9n!vsDa~dN_t%#fNQlu3XrqZ0eji<3o9T1S%=4dawM}gu z90c3g1Zd%AP7Za+{9^qm^JivaVpV0tO0khKv;CszxJbz*6Jyj=)x3XlEG*0~&rUAR zaXPqJJZ-?)sB&VvJ^F=QAYa|v+qu>>)0E&2%f};b>Jq~2kb0TsRb<4_E3lD~0W?ZL zx?oJC)R395#87J=6QPc|^?A#JSFw++nUSNVr-8k_gkE|^1||Ux9v1dl_ncRGe&aJV z;W+fPal!Yz)R+-57)25mZqaBnqXNgDB?|IE0MM9gDGeC`b7NY>HEYNFJ2?1gfUJ}> z5c4=Sxq&f%0OaGD78R2Zi3S6k*Jj-STGv$9KuB>88wl$gqohHpRtmS=cqBKPTV`Qk zZ55Jtf`N%$DK+xjk?QQK03}G-b0}}k| zE7hH9i}E=5n9&3wUUC~&)eT>A^cF^*sIfu7(92{}J_7m-3GtRsbVOQ|869qEyR*=j7vNW#gG~DDO-Sq#02mACOzHOEd^K zf(nx{ZH`Jz1isgXjD2pJ=1-<3U_Js|q|6sPq`)3c-9L!yi%Uz3Dgv$FMn-3IyDp|w zkn6rD!z{*IdkWU|wZZJFjP_1!??sI=BIl;*jv4)AijtZ@+_@RUeqv*1*M+vUZLO{C zYO0fpVMZYl6DKz^=k;erRwR`i`4J93ZDwRKY64%7Pt01LQxQD6%*Vp(m5r#XYYB3> zP`*^d4(x&+_b2<~laUzZ;BG$do+a5(>WV<;Uzr?5L1Ag^P@e=qNk!fsSDWX3PoFVt zETb>{HaWct=$6M#&G5Q%J$^oU85&mwe@v=(`GR=~Eu-7*-dfxB;(2@>ureCkyQ#U9 zPM2N%eL|jmC~)+hM;lk(5tzN8=90;S30x2vqe{f(#gn9xx8t$~ek;<`^gM1|H(x?+ zHoH802p@o5iN8zD?ZZgl+ugF{(iyT$#uzF)rs60CD}Qb!BDM{en^Iz`^s0sV*sXBk zUR9IZ1L8tD1~C3B@LL}O0k0?O^487!s|$E*la!#$I?mCtWv7O&c3QGGIo(?V9~vH2 zMH<%ZmkfrV$%gcUbyOTTEE4dAhHKJ+C-XoC6A@DS=6D3;qw$eQwEzc$iz$GPC^N&kv!uF>odXA5)4>(NK zY+Bxeyg9FYVP(;H(Jz=@KW1x@^Ea~q6<@FJ_KSw|#@eW9Gk>sfGYv^g4C4V_WySF4 z69Cuer_xbK%to@N#;i%&siDl5;a@37l!g=uz% zaLlAmpe%0l%4mmKh2$WBs5l$FyPN(UMmtE7q9`8*=#UY~XDkwF{561@;ES6Vf}w`B zxYG}GyqRlV%%GopgrAYX{(&7Sv-80K9Wg@a1mNRS2}+6$MAZ?f$Xb&^XVXBc{DQz5(#K9ZtTod^s24BH@V!HEq#Tt3JamS_rTrSAG6UgH<>TCYOSO#zjGkd8&DaB7t!Fw|4u?1*Nn9m zdacDZ9hzxxIdOc7>BMC)9&Y7yWZ?99g2bg_J$Djv6YGOYc+w^z8!P;S&q5$u=(XJA z-u^9XLEs*QZ@t}l@rKTD*ID%i+xqMMrz;`hT~^|j z@*yGS*WiLfuGifBmTZ^M&sMY_hG_SNFcxb)`zFDo0i+c~YC zKICz_3=3_lRz$P%-tnIi(J~G1YO7qksUh>(G7xg$4tWUG79f9w-gUsER}k~Ld<9gT zgucDusyO}hf=B*ZzC2x@r?=qmFv>wR=-Gvf5|@UDl#SvlhBM`{7c>A`?^sgEF@0_% zs9;4VR-4r=A$Suqp|Fj#&)F8j8-RN3HbSEzF0Q1CUTwg$;)r5Zdycep%OVJvC zC+s%GgD|>-rVy93^V$jGIovNUu0E%aQ$IQ|oY2qOm&vhm+*56r;8#{b<9cn*|OHARtwn8Q#EAH4nu5b_f`2ddFHTN=U+ zMeVxAO&A?~eg<Z`w< zHh#Ome8}&iwH#jG`ts(tG5@i_5&vnp8{9EUdlnpwQEO%vx}1&d&iZ(Jm}hNi2|c^~ zv=d~yB;keaPsEO1G|t|l+d=5$;1Cgga8Oj#Bh$(k)==E)Cw;QG$VO0ENxO3PGTz)l z`t^C_59CF5iOoU!3c8brM@aO;L(?L9kLvyg*$4W?Q#3(p>ZcYud;JE)(bN=h-ee8Y z=>G4%_&^gYTyzghq z6;d;@44+>Y9(yTdNN{j4ZE!FIJUvQh;bIZs9qi*%|V5Z03Fm`mA7C#BQe-%-&I4sVRI zF>&x_4GPWLxVXJ?!+oqoYwq&SZd`Vp;pPE_c;E=)T)oV&>o?LD^7ddjGm?Wlb;5SQ zcs3$~VFTfvYXYk+oJ*>-*uENktV-RpgP&q1Plm> zT3SHs@o=VaRf`NqJyBA=W6;_#-c|<9G4fm#x9hGjwc`f6JpTGvb4W*no~+v2QQO)Y zw7kB)uBNN0u0!SPg&u(>!p!xh91Hg@k?Nz7pw|?c_s(cA!kpbv{GE4iD3iq&4Ih)E zbvWiS_03biw51N>r1JNJvwCf(_UbCQ2h!PXlNzY(3zBN-j;>tyom9_KkzbIiN3gGV zbw)gQ6PPS&7RMuTtDiRACGd2dlRP-PxQwll8S3sCWKQmH-JqcCZpjXRKS9S2d-9$G z85KLDJALGTT!$iQ@aF`^_4#|sPU~r7yO?{Jy4ZXg7bw0QZr$GH-}CD2!VeUrF?*){;-9<#>Hl&B6{r_d5LY$mx28pe8JfgL<- z?`VIlv;TG!KR3rbbbLI@xHYJ=W1y|AUL=bSydBK>?F@?M<$%b3US6IPaXk*J6(-$j zj2LcRj2H&*0OW#1*T>6G)5-SI*C~JjhlkZV)H@WP!^Zva!`rx-SZ0PRnpx&>e#vl+_}n2MVkts3jgPxrRv}()YU^~7b&%z0?UdW{6 zFG4 zt7XW}CVJ81bAC9FX_It^lT6q<$(N&wlwJ*RkQ@o;#6mxScuQXw9eZ3GcTrusw6W%G zX=!ysaFy&Jo&E|_U(qpuQqceUImERR`*0jqt6!X4Br>0$wrD2CiEavFI$98*iZ$lF z4|0mk1oC6MD&Y z9ZeU#)4IAHi9NYC({M*V>Y@0l#TGfPvtR@()6mCIXEuRd-R*FlG<`bXlP_fAeuXVc z(Isk*i4GUrKR*xdSm0(v5$DQLj5L>gRub}Vmtx2BT=!1DnSy79!aP^bP``4Ez5Mpy z+X>X+exBxoOr+kQm&k4LKVN{4An%BgXno#7zGxA6Z~kHfj`sMAeq)vA;Ps@KMpmkl?dJ1`fiGzo2TXAY3%>MIaQd2@0 zt5|{f;*)`=on?3Hm~EvOvaee3C>f)ADhlDjMUuFf{)LQ6^9|drfnS%U4%+GdA^$`+ z&slIEM{|E8=H`)|L8xl-o7VD!pz9pq5j(Z;+4uzNs-1r^_g<3p9o&8;c;?Xb=cd(x z0&{CTXGV}t)cx_`m&9QOI@qrRv zXuX*^(aq3WjqoWx(+5~%Ovy4ir-_Sm|3tiDN!P}%{hZHD(2!~6uio0P5SSV@4;gFp z2@9M`c;zvb`*kd0kvp|(aokD&OuwA=E9jxdzYVR;dl))$*-+zp-Cw3s`qfV%3e7zT z&E14LLHJIG8r3D3%dc;`RbC0Ika}tYElDo4Nj+5RNXnf7V>QnV%EqRno?#=Q&M={p zLdQ}w?5leal{>WRx_C?89~$pTH1VvmpPO14wD`4wV>4jx6SIoz@`CsDy}<` z3f|s*U)vhH&YH9=LFP5WQXEqx6Sb7WlT>O%Ubkn(!021?(Q3I*qZZf3g;t}OyB}lO$FK5{{Fp=%OPf~J zYa%yQs}}1G>lFxmQxz+8gxH(kLt}APGncKq(Dt)L4aEqeoEe#Jas7VZ4xwG5BBIbZ z;ARb5*fClwCF~ut(b!s>`a8uTqw!Ac^a7$#@D#cLRuS;RjhFV)N2?A&{kZHPY6b9k z`=O%5cfsrOGUkx&TO^+j6fk%+J(h>z^SOO^iH=lxN;T$wW2zA_t5m^OFi|tCnK{fd z4)~ee+V|>j*PI#QBUQT@oMGqrr22TmZYllZQKrpCSFTo*9YUG8>@hb-EFrw^mBpM6 z7?zHycPi^fD{fKh!(G3EAlqz_h06!Ck8)gabHk?bDyIau?`+<=9&qS43j*-`95Ozl zVR46N$Qo%KN!ym7xGLn8Ni216xe!FE5G>4-jd-|!3b_Ui;BnEN&H1VQfO+^Hx81&o zhMR47Ct6*?FpPqvAa1R4IS8GbE(@=7*VjecB)r4k< zC!axWL7Mzqnm*Ud`Z2W4CQ7^hOw%PcO&}fK%IqQ6+$qWW-tPRj7=|P%T z9Hj;!QwK$}eGS!ExY^a!@b!7uPQ-V&v$4M4QP!$w5>b-MaP*sMIrl!L_~?Vu`EyRO zs1=$b4eu+5vcVh1%gV^sw6?qck}z6mwejUu2GVfyqSgu+ZSF^Muo`V@CEHv`OZ$#u z4ld44)22yx&UT%Kj2#s(GL8IjyJ;%{UURP!=^ zo&+e_0${cpD`dw8XF3h$*^s%b-`ZKr5JhN|QWG9K-=VAQ?UKiH;zQRKwUJ0jV4RP` zG^ZVJNuikvNePZ6Wz?VrJ4oSVD$CfhpAhm_9g`jOT%8`2H! z9K^3C=ItHz{i$EESs>Bn5LD0eVpJOvzBGXJIO>6uz|z>RR#Ed>`lWs^daGI`Ap1d% zrCBO)G0TBuoxfp>SFFiFuCO|k5sJk^w>t7ew!5b<9(5!c4YLk&YaL!hk~4FZ+b8?_ zwRpYw2l-pgzV5IFvcdDalT{!#!P!+c}s9ThVPft59ETi|M3m?E_g~u z#It}^|1twJ^>7{6WCj(NAB^32C%A>fzW=(_QSU|S))bN5qy;(uPX<4Upw0@z=K$L-gj>or|7A|!)99l=t{A!xy#0+_A4vB%r%UA!v#B5fSlg`5GJ z25n9j?yCz}3qtHh$8TSP^#c;Ng-Dfcu#xot5BA;yD6eMS8pPe*HGyD3Ufd;EaCi6M z?v~*0uEBz9f;+)og1fs0m)ZHw{P&(albX5TKXq=Ms!58f0QK(8?(W^)Pp@Y^Ycx;3H#X)|byzPE>(s^3t>b3=D zIwoZ6xKu&nkV4PThgu{BYr()7JW6hpzQG$M5sh&)VzCn9rY!_qdBB$*)OxJ;dA?OpLa2t&KLqV4sTx&*9f0=`bKeJ zyKx=5ub1Q`x2nysd9DrN@@^_0L!bKQvVAmdk)wGA4$cI8HS6FBP%ZRyN{-Wevc1Aq zRi_S{i$dIa;ywKqUj(1pr$O>#f_#uak^ZmW$dN<7K(0&$CI1%n`)!2*=ihOmS)S?7s!5 zIscB$&OZUvEdO%u*nb76ks{H+z}&pb(tg{t--FYCM-=YgwrRg#VE#@C(?2!s-!84d z{`+<1@9;HQ{!kkKP#XVG8vjrl|4|6au>KzAl8 ztP$pFxTf_?+?sMQBUwO+CGP%+fly_vsHy^AsO^hL_;SB$bJ3-b?!+x-pAo!;tsjcz zfZ)d7ac4Vc1t!umE`>=$9U#AmM|W@a|FML0*2I3;~bSJT2mDyrUIF*0$|B``we^y6tSNY=$zA+^cR2zh4u zf(>9ej)+nLiHTp)Sb(MrCUVlz4dw+lrl!*P8|mkW&xnP87%?rL2eGF2cl3YB>-EF) z2L8}+XV_P1QzJ{uzOJm`ci3nbW|o$e@fx3q9XERW1+%{h1q*+oP`gAv4*k}Km%L^? z%_?sUWhnj24BVKd!|-%%Z*Obs=%`C%5`mM4hl`7wdv0-le$ZGo3CFY$g6$2Px%?Y8 z2-cUTgQs1Ar6{B@q!~O+Wjwmn$jsI}N_l&r{`MaQxK|tG3eYjUsHpi9BQxK>&jaOu zqcC$R>8bMNxr&$8+?F^z20uZJVBp-227vZtRO?Z;BDU&g2@WTvu zsKvnCaMydWo(4~kA@3&$$WJ1|-Cf<36qI>x5}N+~{UK*(RpT*h9fAUSjrB!53Y4`q zOpvTi5UfmHp7WD%CP7DxF%#l?HFe*I%C*)=j8N@Pe_q@1*@N`8Tv=`YVJ1pBn$EiCzhx70jzbRa&N*tB02 zhuG)@L~Dn~)!@jykyP{}6M1u!M}tTs(oCS?XABlun=FU+obiyoh8|;44>481?8`^U zz(B#?9v+x3^4i+U>gqAs$?5Tlu@w@s5#dpA$QSXROaZ@KW)u zb>{TQHjhrW3GpyUh?5i2l2S9%_2iWmHC43%`AJy}-nC&$y~r&H9} zbfHvokddK*_3j>wao6XKIBa|__rTXCgM%fPY`B(;dc~#9ntH1pA~gupvwG#F%?^#% zh9h?+?@cr$4RmFLD7fCzB8@k2Eof+^;3>K)@(4wlQk$x$`lsiz7+IN`9PVvM_V-X1 znPk3w1H(MS_BHwNfRG@)f{mlz>Kz>_JWW;X*QA{tOPNUIpU%mamL)bz5#9 zd#Aon{6MfIP&HpOccEu}12UIUHoYna%{ROCLyGuId${Nt&`=gu^NZ*;mRn;7KD!K>k`7$zmj%Ik3q1 ziUp;A3#h_Xo|*o^#`;y;_OxMsetu>qa&H}&*>5~`bTTtJcor(_S<%^4uuVko{HWft z^o*r9#ow9c&R2608=|O-Ae!_JPxgXm(!AltiKJ<7XsNl!K%e?yf9T>WXKTB<>=On~ z5(oG^p4#Zy+REJ=YY|(&8!0##yx8!nE1EkabE1YNlmcN9Fd%Efoa&ng6vLOp+rXIg z4(O1e5oG4{|0>7j)zg}t1B}t^GcTrd!jEoFJ|2$;GxOn+yWX{ti?%|NJ)-Po2Q&hTMPDSb7<&zGh;DuI}wnHB6G9YlREpNPWqAN z@$0Av(&SBB6inkL9T48rmH0~^Mvk7WJm;>*PMn7Z$HyxvMY~C;`xh4v3*i zoMp<1Gx=O$gT;HPsEP`UNCmRC$?c&@1hTTSfC){APtvkDH9xVS>{|RD;KZhe32$zO zP7HKGZL+f!5vOu}49udklwnujy#uVP3%7GXGt*D=4tbZdFXK-Wp#%G=^VE))mzQ$r zV{Q_P;Df}9oHxcUfcLuL)wBFd<|rY9?~j=rp6&eCVt}`HwHYNVb=glBrcS(cCJUnv z7@G0CXd+5tx}gBeKew^9w#av{bFNz)kWG`zW}xTw^;h5e>qQ#F#mVB!OwV+`vODUO z_mwmAbQH1vTJwq#GEa%DQJ(o)xO>jge7Ef*z#*R<$eS{{lZBlL}eKTf-<6^V|()VvCQ z#V63>Va&=(aULI5EthQ@MfA4^}MF7TlM+nJILq}&5=t@;JHPs}c5#Aj9W`ViI znF-;*!Z&K>vK1(_BHb`W#~D@+?o$v(li%!n9H@}WY1$JK@l#UQCxQcpHzpC>H1za* zM}VD_EYQ-j6e!1p8tI-ePzNUN`^$-puBy)lL8iEvWrku}V<9pNRUrFf4QLaUvf(S_ zC}~~_<0QQb@MI3#6^fPfm@g0stsJ(x?YerNjnd=1X?XZYMuCMTWn^@0Hr?ZR`xd91 zoai7{_D%?`MJ~^K7;&mM4hV_6m8`n$GgAV*>|CJwGedXhTPu^%ip%E1KTnY;mK*EbL;t_yNN**1v$&y?9}`y0hPKI=BFvOVC&w9CaPYlnhshX zB}H)9C7)*3v(BEYqbXb|M*rdF8&dv@TWGA?vuENQ_r|M@{iCa%Pk>MTv0W$jYoM#7 ztJ+Z5JDZtBwDb}2>8pGtoD@BDEw;bkCq-+IQv7j{~hnO03sjd$V9DF!W)L3A1F`wxuPt-P}& z$WF`jt$uVMR)Ohx{Jx1R4>p}MS=O%Y!wghyN9{P(e$D+>f<(nhMGwaDuAjadzIWzR zCF9RM*$_>e~4W`m`S9J;5*<@*C5T9B>(uvwz6?Z#Z?_H_2q(CAi zARds2+E?W2Ecb2AK+Iqw@C$f{uXh`h`!jP^qXyQ|ZbOAvFdtcxijh!W~x8hjK#t{ z&Oz}I5Nw3QW##%JB3gU|-z-Oga^KGdBq`PG4=|?My!MyUUF@bzA6`gJ8y+WB{Ddx{ zL9AA`%%8N(d3D-?Q|p%E2E!Y^L~>s;4oj#p`8ecGJ`#eEap|hzWN(>=?bZv*!Dx}q z#1?3H&!%3WOi>;|7aU7w&?s%AN0+c`jIL`4L`)#x7w@&~nD+HHI>GUA=ptu!%hH`f z(j9*;&_h;7_Sr|+;bFPkfJcTK9sd_yvpq}LhLr$I*dH!kvZpU6W_zk|P45~{`*s;` zF|oiv*@5ZgRj@0?9P5OOi)~g_$Kux`T3+FCpi9ij8>JNox#6`S{rl@+=NZ%*q)tHh3PuxEFXQeYB zC!3=VxA`0zRyKutnVC-STf(N-#}sA11qU^JbtD8iJs_ExX2;*e>qaq)aOhYwPrV<` zkX2*;WUIIl4ttQ1nw>y~-o{`i79fd!7t0Bvkc)wf?n^~CP<-d}YBZuB^HtZTz3ICY ztXR@e5ak6_=$TsvK01}?miWeS2(J(YFUkLpE?tQ>+8J8a8S_PLgo}U46u-wcoja{~ z`L-%jA=cjPv)M=s5OGy5=liosky-gXP(G28o|?A5EyNw=KJmR&O+Vm>eP zMM$@APi}i>u{zmzUptX}@y7hAZu(mAH8FijwxeUOlf7p?nFI-FpQ(Yto9uucBKaQ7 zGIk&(;*Sn9#`Hrz!oiLqxpu;)tdO3o`T{e6lU0Bs9`-s-a0m8h;f5pIPX^-Ur|wN9 zE7Y>Iuj^)Lu-A9DgvZSg^!_h9W(U9KW8rJ7Bmc-N*$-ccd1~sPB5du9yNZ`&OnaXL z@li(FiY{_=W5_R~xk2pGvnY6=A`FU8-t%8S@qvs?fW?AINjbL!WE3C0o6T71tzhO> z!1V>|^yUGs--@~R(d+qq3kHNO+8BY4YQ{rX+sd40X*}P3tNg*LGL_E`A81LYu0FYS9Sn~cz zv4K4wIEd&xyU={HLYSE$lnD+x>IA^h?`3H-`t+%L1(MrbHv}3buUzbk^3EVkB?NwKCDR;Mb zh^BM+*EdGzY7fi^R^5S5x3hZL0vI4hU`aM3i9-6hY-3r%Q%OIp*b@jlxSVOlAQPI3 z$>XVS*xNBZ*&CY@Dl!VCZ68T!VL)#v&4 znb;;<1JHrZ0=D~OE6XND?{C0{pOj)>SIx>cmzbJI&06P#EZq^2&Pztm&UGoRp>mu( zYrBlE&!NLmQC87jAS^O{7SjTeT|C^Pqo<=`T(6D1)ad*)wop<7N*$;+0C(VEGBV65 zEwJC%L#e$!6_@*h3Bq9!SF?Q?PuYritPyz2Rt1d=>NJ;iDq882%SMk&K6?2Hn}g(Y ze~a$ApUY+)DTAE+{AOrOA`1TJjf-=F1{&yqI_mlP=M$2VSYOAS_RiYIHZDeDf=^R^ z9I~vjd0{rMmbR{rxa<8HIuH!!W9Jb02$}(69lBoE#%V`Lgz2>c!`i~cVYZExZcokJuWiazAdx*P@MMYB>%4aX+&z@7ceg2bN;$gBCM3P z!Em+8uSO0{0{mPc@$s^XR!!eXw{DRx1bGHJ=lg)s_{vZDByr#zT>no+(nM%Ofad-evU`SAe7!7j^Dq z@0_sdm^2fT%=&zZ)X$OQOxOEhaVnA#UiYRmLwgH%lkL~vX;CF6#kQmzhP8toWx=_Ayx**>%-#=mSx-?8qP^U$F4h z!o&;@6E8P28;Iw{Y5gu-v|^-u~c8ETbc=<;MA`wCI4!P0xPXl#+swq^Vi7+s4_L(-O1+HS|fLd1`Z0p zzqU&sr&;a45A-O`jlZLM zQ!|>Nno((OP6hbT^WmkZc(Go(xm*tbHN=1h1g+l{Uuvc|@1?ne;ZbKRxNu=(?zNZb zek#4FaDG*0HEZV#)p~lp-rf6*F#OK(lsv9#DcOPj#=z}FHZ{1kiWQ%PwCeMLfntju zq`%sok(NP3DiXVYdQ4$lF|C}0yHiMA`|IpV$k((`H#OIDfFxa&6vdzZO~(Q|LO5Qp ztjuuSY3XF25RbqI%&M1~gx`yE0KgW=yfHVyb~?|-1@*>`QG%osYdA?wf{3cixnprVQI9r)Js7xa@x>d!pxhbTC`IbEs>u7lPEr-Dom8p4YQTV{X;E#z& z1R1aQVm3lFIgQ$@)06)=XV0zzCv8s7clOZ$jHDkb0s+*KliFQO5@IwR-o^p1=! zE-$-!(A>L8D1#@p@hmUb^VqI{F(W&F#eJ4f!rC&W#rWK9cNhUm#24W^Ue?4kf|>eO zGQLJdpvJhGb2j%5W@LWwfZ%gswLa_;II+NH@-jX=H#GIuBY$>I3-2rHn869ou*zB}+NqKpz z7qmj3$VUYEowt;M$saz90w>oD20EJIu>s!13^Ys>V_t2dfYEmVdScr5BmaZ!8&(2p zXZ=qj%sl9^vrm|+t`0JPgb<-L+=y3{Gcn%I8U^I| z-qJA8(J=~mUYErMzrq$g7#tcJd@mISX!FVKnwn(1mkEmwg+snF>hBoF+c)CKqXkYe zlnrY}c$sx|1{TuztS7qs&qBHA6V>noG+|c}Wk(rHQ|@C4-eCm#HoPo4@t<$@j&awVQd(F?h^R~>9w6@Gm4_s{{_*;G@0dck(}Kok0-Vu z_q~ygiP7%f#>wlOpQuRih|ytDk&$u|((lFcfqxf&0gfh2G|c4u?CjjMy!5ZeSJh-i zdq;Z*GfI#^puT7pj@rCTbXC@N($1E07%7qQloYMOBccrLCB9bgM+k^doigu~m7S!e z>BBky+5mKD#Xa2mr{}E2#Ee%}*^$JllF`$%zIw$8*K&K;-^z=}%>EG95I0kEMU`W;ZH%tW&TjV=6eI)IH#D@h)6?DEL&}@$iz_=< zXBVd`Namfw-k|&R{&8+w4h~A?wC-R9bA5|x{Q$Xx1AILEbbo@>^2|?pwc(|N{+rv| z$Hxr2z}p`qn2!$B$Ty)eePcm0GPN?!De_d56460^PX6KUDQud@lRMb~XW2Quhd8Z{ z&VG#&B|G}|?OQ+z#Kz`?_f;v_?m0{*RS4#LyLQ1Hs6s37)McJ2cA!UN zFn{2DVHT~fu8SM6vN}FGIwmF}`jsbwh4u90;n5;Y9UjE!Ncjw8PdW#^C|T0^Z}*sg zWGhbtf_Ldal&A$pWNC@t6%@#TIzU5a9OYmhd0}W_U}I`(Xl2|Wg!Hq(wv?r8vg7_y zwlFA>V4A?`4WBopzLAyT2Ly-V5e6_+R2n*c1Ao@E)YP;zJdpm?cp}qV16_=;VcjQ2 zQ0MI@RnKAgyy+Yv3-b>O;S@GVNTEal@PRacZOVO<=Emmc`Z<+FQZkZKWTtujM<#;R~f9- z3l!Mis9O0U4@UkNh^PMQk21GT?hG9xWchy>ABm~~cX4p(s2=jxGnKicYAMPe%rM6fn@_Nt8p#i3C>LC?Vn*g4u11x%%KZO6-_3f7#j@GYIg|7#ZW zzvMVw6xV#gy}h#xf1B?9ZRG&h->FFA_@{g~I|vXb`WNShGu+iw%qHtGHv@!~VW5pM zVubLaQ>nXi;6AdzjaYr6XX1n^5Ryj|C3_<*FZfP@B8&p%IHA*+Pw%*V;bHCUn;+G8 z$?`2VU*n%s%W?|4C8d@#-lyK9xO!xQU{L@4uPe;k3u}52Kff9HmYGwjyO(mjMuFRR zdgOu~B~bk1V`z$ljY0AIBs}I5u_KRDN$p=39|z?`ZUt9 zG^FHDu926FEk}>q<&3meUF)EcMfFc${;yx1sj?D=dz7k{YY(uueSJigvN>P;Tu)OU zIg-X^IZ`F+=Z6iiUZ+*t8>B)#vKs#w&1Lg;b&CVlmSmaz=v#St=JKpq8St3c@F_ol zjK@M;kR{R(^1z7mHY)vA&|1)HS-k~EbF0nMJu=oc7783%6$bcqWUE%Y53!^Hx{?h^I;3r# z@;u{*wKH-aQ@ilC9upI5*eH6|rt<^_2N&V%0v8EG;EeF{&T-d|R8+hzYs|~zlXKw7 z+O+MvP0semmJeRLLj?c`d8KtnRXrRW)SGc)Wqy`F6k~GEp&7j&EM+elc?QLAFGmbyYPr6@m0}OuYaHkE82CMgDC{|5?oOb1&BJCRHYTBzaradM}*bfYfah zvQz)7PRNhKl%F>-Tnml|2DxJN)U_2=6*PXH9vlF1i6zi~uOWg_XO-1nnoDXvm*qDw zy$H(w$g0x`wVy!G6#_5ZApCJo+XhHj$ObWTxF#S+5$d=Btv(+p#`knY!NN&QP$Mbf z*Va<=-C6`FAbH}^BqDwtahPNi67{yx)3Y-q75vH?x{AtfWM;Nv9~^k;MwOOK@-|@{ z<<<`q0!gfV2DCUM?R2{`mwCM(zs0Y?(*SbQ4~Stvs<6cJFp8Ck&u62#w>Q2>mJL|% zXrW{?x##C@mqRzj38SWGRMk`xaIlZYMx~`B$-dV=+_^Pz@tkKF)~0cs2O@EZaaylC zMGNzU1=s88!y#s6rN+BfvrX;)p#@^C+Cr%)-9 zI*MaDeslXZ?4<+Cl*W0CpnxrE6AniZr}YLRiao)4UH%!I>l*QQ4nWX~I}2)TgpszK z_0h)a1OKv1VQcHAHuZP+Uuy?gvr9!@>yz~2{!XD-_xa%D=SOWtUVd&~&hg|MELf6_ z-ppGFk&NVoM)bB+*MzG6hAmc%mWHyD!ise_BPFTD`KZxL9i8Q6KCD(?(m;T_{3ZJA zW!5+U4acRW0?2!%V97~Eg(Ge9nE>&4+gjR1f&Hkqr8wo%jQ5c0f`TE2l7%SETLEP) z--suh(-ynxqWa3y$!(t~I$&XRfC&1K%=q)?&ui_L@%hmx($Y#kWnJm;_a6+Aj%l>& z8=5E4>a~VY*>Tl-M$)k~70nTg&`(IQNU+=KD0wQs9LXQYZUR1A$C*0It=>;+FfhJ& zpBuu!z)R8e7k%)E8YWj~7nf(|R~DAs9wm-*Yd*IeMd+L>tUF~JqSzbBXy~I;cSpW9 z$!x{623}ja_lwOh`WD#mJ~aj9o5D9Y$@j296}p5Z_{6PIz)jJAsmZmuIGg9ZOx`Tx zaxZAg#%-t+z}}q7NU{7H9NFu`z0RFMY(#=lOHKwFuUKJ9-UzYVtc;IwgJ~WZkHd`> zS#8gqK=S#}h@-d9cBLf{44e+V!QIj8v3Krg?0RnJb6Zx0qnX6{ecE8$#|%E!o^JDy zOJ{^in#Xaol{_j^);lT_AbKa%$qzURk$gYIEe{}gu4UER(q?L>+y1%h6V>PGn3%37 z(iDOnVNfSasVNOy{OsJktagp{rI*rDo7GJR7aNcotUay)1n>Ohrhq)b?2et`It=-~6` zu!H+eAZM=A`t)g5%yy}X&;4fQX@4IUmBtk9*M@Vn@M(Yl;pRkkrOnysYC+!@z^p8% z&hHPxK0i5SVvLx{Xe#Zo2jZMVBNZhp(p%n|>MBwg#!b~;O1TBp*Xivw^12=0-$LE- zJ?We7OdK5<0rMfU$d0ir{$+)&$o5;XK0KAW3qBPwS|?qI-!K&oP0AG zrz2RKwBKujY?n>n(gd3Db}e!@KfqK(#jPoAaSS|N?Sp{3#db^;MU#%jZ7Sv8?#=iE zGSciM8zzA}X*JfRCg>b*m3EjPkA>n-5bAFyOxz$CXuc=*!WF&tGT(wh9lyK26KJxP$2l&Get zVis{G1=(#>uYNX9JtlFI%&4c(A_jy>a8&>urp@jAaXwx{_3_8tS;uRmkXe<)Et7%~ zh7H2wSq8V%uAoa51Y%Y`ZwO%u!+1~);``EQuUW5aVE|{|yFG56LN&|g_dJiAc^-(! zMklMvhob)+>rkoN=63CLy?(X4xS0nqBydOBeO>dL>d1H~x!5UQb@atYr5#%BvzV+L z3U{BeeRE3ef|VH}&{YuG9gYh3hMWax5vP1!R{>DIPg7&Pxdn{z znsyGH>iItXLxA6q7X&^#ybLt6wz9H(8QvEeRGfEDguwlz{fLTao*xH}x{C+W zXDwv7d<*y8bg+rp+9g9itC_bh?H5z=)n~uNqPFuir!gXqlwx_T)N-8a1s}+*V7?& zx9-=BH@si}WGcmj&+ldQF$rs-%R-#Iv;3_3j9PFF5~BU-I_Mf}p~pfdwSk#;T>K`` zYr0Om@YY=mDf=b2@O(TA$<@L6<{EWlwxZ@zsx0JW8mh^JjW)@V+<6$$NR^3~+c*E2&}&PxnKXEMC{0)5QEAT(T{# zt=0~^oLG1{{Z^z6x~*~8mfY4b(X4cXlV?~n01#k((XRV!wlD5eh_TX_|Q83DZXrS;ENIG*(X0|IJ_`l zeNH{xHc*BxIx74lWza5`>NH6KE}}({Z;#!f7w0238dK42ACfJ%rW7h0D5=-Z4;K6o zJ%9p!;Q_;e5SZwqr2p<+*uAfc$w)AOnnUl(CSP z!v>MTl}5^CGGP71Zu$9Rw5>_^%SX8ofjd;{vt2Vz-s`-E?N%Sl$y*30@9tgXR*eR8u3JVwKPGU^9*dwU#EmQLkO8gP zO5J_F)gMj~*%kx>M>uL6z&33++!74kw)mEE#P**IDb29-K(HHYAhY5a8W* z&j>v{-D6eju#OBtQ-DIt?EhB z<5?zcBTt2?7w@=ry~?i|jfV5QpvNJTAzZdcu8}i6=I-@HJwZRDmqgRsOK3Ar!Zxq@ zWKtc)wp*g^>cU58hG(-ENzHD9TLCTq_Pe6`iuDGWyY$2S)6MEN)r@r}?Q96&%dj$A zPWJQmM#U!nl~X2dH4*tpCja&>J0$ycDvIStD2cPz^W8VtNu)NMKc7J|L*AUn>sXh# z0WVNcui){TypQtTOV;h&*V}HgEPO7!-30C4SvmB221Q2^${h`trIE<3EPEsLDh{NXf zbD?s;4sa16W8r1sX|KCq?42UUWkimOSj|fFxk^BKnRjbETPE$TxQK0ATa6<>%QCGi zXt~=qV}G2unm#^{{U^{ZQ!EG;0Nn_KDs^vLvDlX;zArB_z5$q=40M%s_x-L6$2#7S z-5gXWvSYltxIHg@XE&uC?J)iY6#4|u8}sq0`^}gfL9q8t3tDYFih$i-ViF!P$=2@i z$%X{T%b;6L9ck4NFRST)ft0ViDb=e9=!sM>8T;5mcE%4LZex2pBfN=- zCLkN3L)ks{zmUq}pmij8z2SIoV%DxoRfvG3;p;Ox_~-lWOX^zGq6kL8t1N*jrj)|UK=0>%u!jsDEuFwZvtb_-52-tA6WNEsJ0thx0SX)hJX#?)e>V%;3Zsk zo0R1(YVtlEe^JyARh;dUdF*<`sh}(@Iyp=`ss* ztNjM^G8!k!=MZ-Lw2duJ0P>YuGfP{m3l#W#T7@q)_&qAAHz>_jZ|Z8HWx$~I_;Zj} zTi6jMT358!ZPQu5>xdcScpA%~$m9BvlH%jc;cTTZ%cdC=96b$qxOx5(9meDSkqF89 zRZG!L*SsmedD`qc`C~`lEyV=|$SZL~^O?3LIJ$kCr62(J{M+csUuZs1HwaXY@OnOP zd>=}#e6v?cA*eq9|DTZleCmGjfR<4=WOCEKXMbH#vU-K$<34vrTS&`~+ma^;Ed+d7 z+>N&-t%{s`bSy_of( z0XqQg&NG#GC+*J+{1XKP153$iPi2IO_c4Dde ztb5X^A8}R^_jq)*l@byosK;$SsS5NkS*fdmW3Q-4$kPy_zIKP_YcoAbVlf_HqTXBLNuu7FLjxDDqd#?{d2cH#{;CNvNAHuc@5D( z;+DFjFxJl1H=~7*nV;RkIVS1M(fTEqp{1m*<59ByxR+qG-R)}!Bq@7_?`-6%bgpQ| z4=kTM+CxEmcjx8m-Xu__PHQr3P>^_EudS@~QX?o7NC{#zIGP!5?A*@nosD;FuT+eJ z7$W-)N`}Hwjn6IBTJlpYZ-v!p3fGfFm(-PjqG;FS0h%|(WyJzMHQWE~B%`J*No8d* zJx%|7Ege7Yu(skbo0oqnR@%B(YE7@UQp>js4G@j>>GP+$A|mqdi`;LWe0__3_4f|G z0>!fvhkvmsCMIInE!Bcy?isyTy>s&-p#+3^20NcTEdr*v97}P5q+gt{C8tiou&YLY z5MzBAO**^hg$OCZ2L8Oes7bKI`LAMv(TbCmwr4uXPXb5^+5js^1+L0k00~8V|Mc+q zZu45tfa{k#Ysp^EM+9ly=G7X*Txp!{2nA#0PFPfb6$nXH& zU+Lzu>f(a(*2}K%Su1UAQppco6=c8W6JCi<+w|=0qT{)+nWvVN(q{Gjg2?uFcP^(` z!-zA6lM2FpDwE?6>|QJA{PiJ~)djUwsP-O#>@07&7(g8D|HzGV^7wqqyYy01b9F@( zWdv3aK|h_tlamAP)p`4*C;ksx z8p3&WilBl4Cz|~D4bg(cnXf;O+%EUOfsT)fce!asfk}V>HTTt#`1sgtL6)O-_2<`_ z!~)S*ZXWNwoZOgn@*A4R;W&~LM3IWv0>{#Ldvy3(p7yHnGX=wTw}|O!Dp`mGCfrJj z8@HV-Ef=*jgnlJ_QXI?5zy5TRa-1se#SB_i0r&+LqLOlwA%#YT*&KYwxeQBq>xdQW ze+K+`o1ov1c)SqJYngz@qE!|GjupjbX(aY9JWfsB(!1$_+cB{f7%X4*?jEm24- zfVV0Uj)-4dN$2UbWDxM=-spOSU&q&dOGBf>$)Cd^KVOKal+(eY5< z2xbJF&3KFC zE|>d&70mVhlW-U1XMjQ-ucSD|;$micGBP;2QqLGnfeY|}r2Y67>lt}?ENxA9P9o~2 z6SjI9Qoe+o#pG~x67P|GpHnaoU^HzIJy&T7)55yBx&}zp2*}ZMK)HQ#OhU$m%!dz} z9x&f+fHgD_Y%wV-DeYe7y@R5#w$&BDcN)(D?7MB{G!-%=03OYL${qYi-}3_d%q~v^ zy3yu_^x)(%RR`Wz^t==-Y4p(mD_i=#tgH~gJ%oEjP5qjRswaMVA{Kq!fVKUp{@%^K z;T&c3BOwpMY9QlTf7YxW!cdkW?P#A2Kz|-%IJ&*gi5Wj5Mvvvm`M^Sq-Ph7_^zB-otv?5Iywh6;+d;wJt*LDlX5!7J6u%)wfedtU`M8v@zze%D? z->4_3zxM4|NJyx&lbrk&4+K$?TM-dL2agS|85Kx>P)mskD$-Q5Fh%mJ{f;`|rJUVeRg=@8YmCtvxRS3fKD zEj5D85ltBp(@+^)mMrJoK-l}B!3p>ZH--m{#x-$#w!)y0B zP|w&(?OmzQXGd3i_9gn^|3qsn|o z$h5h++1kaVS<<#iNTWUKR6YXyIrXFO7f$O<);)Hrnj@LyBZ414o$a0P-TL1Xy@&cQsl{tq-Ygqj$_R#A66Ah}zip**K%izXs#ulAw zh4%L}$GMhBw!he%wxUMWToN|l9?Dl(+h1~4Pf;Bl6E5kC)CZ~&eucdH=4J#9UN&x* z9v5a;?7>{yOh2%VjMg&)yB}x4&|tI$j{{I|EvO%b@

Q_Fs}gXCi6WY-U^ieXY-2xaIyIJ|HgmJAH2pR&+2z$ z@|U6R|IJ01>z59iA_AkbyPXLcqq>~o2NNSF1V$xiL#MxfBWZ15W`e+|W?}4PPWE5) z@Hqf;k$*9(J#$wLYaZvj^9r6Ip2G*lcp*s{O0M^i6bup=K}TpfZ$iGYkdXmjS8qRDlYpC+GxbV%F*0n!*eZc^)b{O%@e&MH=eK4#(m;O2MIJ` z#^^@@a_)N4ouxG+JED1^FcD8QEBzB9uu|wJW@!ep^NHNhBPLuJLW73GYUTuRf!kaW zSk%coD?1+!D!G102e)5}V&V-<7`(Us$jHcWI5}EO?a^^`!f0`Rud=}>@bU2pUwXUV zovrn{NPY< zS02+6G9E+C3Xu)JCunM6VPRroVr^|rX|6Ab6yC`Yk|@{-d@jMbZuQ+j=$F^;f*{bm zI%*+O0X}@DG~qQwfD3(UW@dSLWF(seLkGO{m&U{kLb7@a3aLm*Ax9~xF3615Pn2B) z1F4926|e#^@tBc^`i$}O=wAv7xQZ>LHEksBW?~3K-n808yTYF@+n{UWpW8f${#OTdjTZsmQFEgw`1-zpbwYf)yEB+G68H9M;p1Cg*7mbttbNTOIcWPSUwT_PpO=lTwyp*| z7Xdw0y!~uwxGDSU)fbF- zv;hd1$mB2YB{

2{EBZH{Xkby;;^FFNjQp7Wzz;dV2KgRd%r;>jwI^P1d-4E)slr zNK~Yh{Oi|6`5Et3O69U;Me-3bSSRPz7wKGB zTiVzygk@X~F*J=;SdxU(Ps2f6>_QofvX)TzihHLL4kbJyRPA-qm#Hyh)7`BdfO3w7 zgALGLMMVRlq3_%qx#m;NCTC~{N&tFzm44t0Gh-p}G+x2*#p$sFZZTmKJxDH)?`&7W zOw8XowHz3K&;(S>bbrO{`=ll&+t}ENAH_sP-8JS(3toQ@ymRN$z_%!-RyhY);e%j$ zwkVUs0q-x?qJ%%5s{xOdSMZe>XkKhJykyZNi-W^u`1?^nE=yGt;=2nTlF%ZFUJdiz4#^H&B>J0%@q6 zK`ZJ_#Z#ZtP*+xS;3Z?dTrfm1l31#C2pv(XR%y`E#iYW@mss@4JVJzj2M+q$Nsl%=JXC+tR$zEIxR`mf)?6Q@(qEdxa15`vv%LQq7$c{FeilI5SH ze{gZadbL|3p`uJ_e08A`@UEA-803gJrjk`T#mf5C#R;G_BCn7Tw1Gb2VKbSx@N|pR z!#;QbJVniP0#@f;x-sk>(~|fNN)fCxRauFoGHwKn2znKn;c|IE5+84-7-WX{-rudD zy0N_WLG@*Aynk=G`W&bT@dpkPUPu22dv6&OSKqC9<1WD=xP=6FhX#TZAOv?0?(XjH z?he6&ySux)yE{FdXQs}adFTExPrY@fYO3<3YghBHP4C@%clTQBx~|{(;ozhWu>5?y ze!ri_o0g8#Z9kqm5I!XIH#3J|t=*-!j3~8OK6MJU0g`_>)eJ~nIb7zmIK4hM!@ZlJvF;(bv)-2Sr&V`V9kbO1Qgh#93n(Qe?HUzj_(%JEi2aoio3QN7kcy&; z(o~vKP!9QR4)B>)G$A!cSwYD`Zf0@%kASMq^*ka+%BC+x^CR*`C_(ikjUrL6bC2eKLwU77MIKf|R1j=Wlo4ENxUBG(F_il`l3g*Fuh7Q1^)dF3nY!r(Ak+RoPRS zpfE9yYv=Q>x1*4j9=|qC3+X$DIm5mtBc1Y`U2btDP8x(Cu;i(V?F46E6cco-LymvB z$5vO@)f(wMp6`y*!r^4n(bLm0RKgLAj7WurO<)Uf@vyi!IcFEu*2VabPW+(ik3DG| zVG^nF6)w6dh)h_b){Z0+Yf>2dLV=gY)BF-BO6ARoDzsT%Q2|d>Rz^cJlAR8_3t}^C zJBhKtt0wnQp2(rLlcZF8`q=c?GTJpd>)D12mF_1n;>m@R@67l&HvWca2RRawAYcPE z)YUar)hGs!uF&EsFls1Jhdu^;iN)GQDePfMO;)c>f!r&W92;dQ*JHj%jgVfW>fOUJ z>vX$2X4{Sc-GXhdch`H*%VCDTtpLznJ>mb0dTipGq6y;k@~UuAH9GkcHSNIC*ornD zQCj{u{bwGprY%>ai_O8_QNIDii6rF(t#-1;28~t>lE)mz!xLqO1*N1c23}7_7AEG! z6_>WH>!%uDbB7=1AfLm^7+#^i9+dvu_HGw0a)eHq0hc=$c2bYcvgJMUT1IMy+qL@< z^oNaBj|WfL?X?vSZ};lk*P+$Z*|nVZGz;sDYdz3uIZ{c^ommQrX(oW>qCIdHejU06 zr93T{jS=!eI`vER_hAV^Z3Uqc^CJ-8UcbFKK{eHg3q@|=9Ug&_VANG((>$5^x1GUK zQ1HquMSp{dg-`8q4%?_>p*uh%e%)KNg)HHAv9f%Bx(;(FhU@Nlpb&tkT+pTkWM#E* zgc!6<{m@ocM3~`1!}7(aZ6fC;w71i?gg6{OW|SsOFVBVKOn7=}I+j9SXi+KUca>yr zhuId(yo}T7LVZMwu9O%oic+a@)U;|;@Hl@MmH&lEqF-Ps!$GbF)_)jU; zJ5Xp8Q(7!)K>Y#QZ-sqw!^=y!K9?-!SM7}cSC3@AL3?NYJNrGnj@82|C6sU_Py`Eu zw&~y;C=m{abpdoXQS%56a1xb(xu&n-R~;=M)k(SfmA%U8=|=NNf?llZHD@$F(+vKQ zS3f`5JN{ftM(DsYtr>(>C}ss<{4_l)muF&n@oB_`6KI}-yCKhH8QEIqeY%;Zue*tQ zvEgm=wDkIwIph5PlQgn4QHaj%jp%@6I1CSS4R+^BR2mWDh*<@z!|mkhAnrm5K?CDC zKWRjEfkC*HcBNf-GN~JPYQ=0kjx7d-{%>}mBxWAsmp zSL?6C8jYTXp4^oK<@ea7rh+kYkF@(t4G?ogNvPv8IlTJGaSCe=?P|nf77{<+^As0zr491LYD=%hUx0kI^UPPLF&%mczan?C3*Z-1 zuGq$j^73LF@PzM_zXsZg@=oSxL)tS>lb4MRa3$cJq*>jr6J@jl1}tN)FgMB1x$ngX zWE*|l!|=sdDJIsk2-?=%@|xbs?zm15z=)mRa}q2?zq-;kTn$pcnKR*D73(9CukoLM zAz!1gVy^$Nvt*x%COdqkGyYvK-oBTd=Dzsr@Z8!Bx&5!zuQVk@loMPOE|I%jEeCAN z;-IbZbC-zw!PtX8+mW}Yawo7t zuQY#l5PJ|jX7{LEF7nobyg_`o>15#Q`A<-j{5+GPS+XL zenZ0K`EFVCp>5@!Nn8}%VKt5e!rut9mF%j!Zj$>LISfR#WlAr2 zTeATp{JHAAl>b*=n86%DEDK;U?Loc8h4T zHLFNNnlJE9hELS}9*`WQCZMUwIWX9K&n$MOL?Qmg`8t|bo0QVzOZ%J3RR$Opr6Q#) zJX>E~Wd-&%j@(7mH_0E0=#;(wsp88$WGe@$5<{QslF3#p(>+>yK$$8MuCe);ZdGTz zj(TF&2bVMV8AkE(wdv_lk||-EFzD|Le+tP`IKhMdjJ+sefxd}gQ{Ie#5G)5XFezg? zD$c_b%2DOsXvG-f-RK61AHZ=0{2#UIe=c9QTOojuCc&MM|5)z-b7uR$11|hu%6-=V zaw_B5pK@Onqu*hJ&AV!sHJ{Y4S2vo#5X>hgyviWaT0qb~9vWN_6CB=mmm)Oxv!Kdo zbLNA#TUPTLl=$m_BVAilW3V(o;A*S6nR-28`zt>>)F~$>ZGOr$=4^u`*_+h z<58AZ_k=()f-qs2|2a1$dQcouo}86OEXvp*-(I-*hg?87c=XjbH5ow@L%I(umiBkI z$S6_avfrd9M#sm;)^KL`_kS806&8I0WdMR&5zUsr!E5Skev?q0A^9VDM=Pj(Q7J)Q ztULTZlv8S|XK4|XdyIqov0NNfGgcHg6`NX~oE{T`0*D0W4TTFwZ%^msRzjvvuYMA89^&H#oekgG$ z1*luuui+68{l(w?#oztK-~Gkk{l(w?#oztK-~Io<-*Nskee6%}-2dq;9)y}gU=TMn zF*3F%VdDI+*GH%?38{^sOWVqPI#7i|CL{ADlS}{J3(wYX%$j-w%(7i5Jw9K`o^|7>*HfieKcZ;1O}GQfsBuo%1_Oi;GYRzQ)? zE?(E~G8)>=vamm_SDkiznX>Y-XgGmTV(-5Ip%@Hc7{y+9gA2mpc;ER^h({)fENnc&4)VLyozJMV&`d3c?D3uD2zdxE0w;qsw-H$tJubLB#O?~Z1QCb9rK%AM$P zV12$!n|Q2`x&Fssw7O2(f{P^+XJPRu; zC!FwMy!Ykg`WTKV`Qqi};Na(p!NJQau>N4GQp}}nF<)>~bMtf~bEIgfd~Y)CiR0hh z9^P{>P)I5m7;(H97{U~r-kW{dGWwih?A+Wa#U&Ev*F`bnxGxBjen3F>baomat7aL)pd|;-)5|h)U3V)RBu4+JdV@__id2OUQaVvIP zg7{S*JAafsL1#WGAb68yZ+2eha=ZO zEmH3-4xD)FR7_6SMD93!Tc7;iWR;-0I0L<*9nF(s=Bp%q%kN|@Mo=&|Bn&FB#_r~+ z)ZQ4+yBs^zjJL0w8jJ;eT?h5}HUJ+Kx|y0HMVIfB$ndK?NfI9)zhL@VjnwM$XW^%Q zQCGAH_#j+d!mLz@4;C(lb&XooNgbdlP*1ufsgu6B1jFH^YW^2 z9P4S)4E;C&9QehxH>=wUCEZ=t+RUoC9%~Sb@1AvPYa6he^UB+XEZRTpSb||f7ep?h zJ$<9`)6!)qaIkBe6a`n-Dv1c@P3wC6j^8Ub%lB)-5QlniDO&qrw8gqM{qwSlsgrD7 zvK1ZgOJVEsT;CeC0HuTq502E)3|Z=uVcab@*s`3@@U7QUKe@F1KRMmQIob}}33av+ z9@Zvn2XbSGW~8d;hEW;&WPO|Xkw6o0cRkNO3FhdyZp=D90S4E4R({Pr3hOW~)3SNe ztNzRS6Wk`%5fV~EbyJ%%^s;K6&CrZ-I(TJsLt9gIedY9nvF&zZRXUtgsd0molT$;e zlUk8;RVF7Db=J_VT%CzSNu;kNW0ux1*8G>%si7rkgO;`z7m!}#<{bN4Gd+VquJz8h zH}j`y92YqkqQ6^ECRijhr#T^|w!eC}?T2%ObF_cBbT{)BSqrb26}h}LfKM^ zmu9X!3rL&!H89o|1$Nf?v~uKIhK;Gq6-$b3Dg#V~s80Eif*57#ys=_9#RJ=$MTgxPB~HO5 zZq#ZJ>{xd!N%yX>u14A0qW$y*t5gjONcs{gLOS!DdEp?Dc}fJpy(WL5Glfkl=D))^ zSnoyp7}em~vL2(-OqQ3y^~>)cn0JK@7a$YhUw1lhCJ#;p^J3d6kr0&tl8`Z>h9-yE z{2(wbXu&wKN$4sKCXGG(4L)8~R9JbguKfwPIzJy6yGuyqL1>7T_A}+KL9a$b-{GTi z?q;q_kHcfL!7@yxIOmUvWCg$7{KbyLtjg@jTpf24R%W)n7KOpDWkM zO(^0^e|SZqD>}W0@8jpZP5VQ)plN1OpO&?ANg~hZ(=Y74f!KThdy3+6inAF1T5BD# zK$9A|`Br!cIRIY=$vWE<{kSZpPfV>@5YC5$Vrv*#Bi*lBZbrXd=1GWnViD^pa-}M} z>}Dd3yF=J5e&$#Os*%{k7abfnfVxqWHW^ugJWxeaJBb$PgQh#PSb1>t{fQp;wx0-5nc1bbW!RpVov ze4y_&lP+zL!&KonqR5k==~pO%aD9S#Um5$O9#YIvoxTtGUmrsW?K|aLwGih7xEDCL z?y=5WZDeG9^kK@^%sLgdm76;Zd42?U?55n(%##GT-8IT{uE?j_6zb|v)F8YV3{eMi z*{3UpTO;v=KxXVNrn_wKjmBYf$wO574PvlbJx_{=FuG9w(1Z7Pryg#1K+58f9MZRNzUT;>rmvIO z7g)}P)dYT#-}>~Yu6vkpG&zU%E=yxRzGY~T4W<)9j?KRj7S_2YOuBe2{E})SWx{M$ zeE3KlL3$(^HUWU(8MkPC*#+@*c|Js$sfIiW|@!)L17#hj`Sa`{f z>01ugbZU5bMXetEvkJ@Fp2P_|wo54QP+T|L*$X^n09YH>F_f0AUnB6668G_=Mt3_hDwe;=%*9jNmtozH6+EU2`+ zUS3n9U)WD`GHXIIEjpcupL@sugv-fWlh!kYoz^4K!gU%Ew(R8`*0I$ZkT$&dRPQzw zmO`01n%jXBf#+smy99x_VY@EmfUrJFJHOO)m(f4n;5}wjuG!FDGHkqC!c?Y^j__@B&6wh5C>)E=`ODZ=?Sl1NuL<`7I&wN9Ny8n$gM{t0YVN%J3^JQW*`2s)_$QjI(OMn@I6)?(6x8oIK5CA{SZxg}w;sq(&5dpk|dNQh!_hs3~ei~?z z)betJ!<@vkM7cGoVew_Y4xWJ9?i|=uCnp(@nLJ9uIMn8x`JP<(QF;sN0_#V2fFO~t z)n{-b-l&YP6*)O2E{QC3Teuf7g@u7IeLjfb+K}4#U=>^7nbR^73nZIrYRk(9$wzR5 z$zPY3)zmIO2ns-rXXAW_V1;X^21DY5lwIUHgwi;8eL`f^OwMEs*C0)zPhXhw@mmfC z4+BGnbOCF(hPy5i+jqHC^R9s01S_wsw9?VhF*Y`)K=Pvsgn_w~)JE0i^wJU|^65c% z>=yBsY9rv4Bw7R~97G0NUR><$Rj~YN%GQMQhg~loNSf=7YmFuVUj_CZ)*bCV?@;c3 zIo?Ur!$SyV_2)h|qm&fQ_^&zRblAkiMC-xT)zMi@K>@fA;7SmwtT0a~-#sw01)>TU zg!jRkxY8ClrabHNVNXSl;4 zHg77aK`%1Kb-0!3+S0^A--sawtS+o9EiJ93B?yE~ct+N8ZfxlxRmL$A?&=!H7SV}B z7XT{;GlsCftNiFm?Lq&b@OZ#*h>r{2ilysh?`UHO`IDh!7DP=D`}E=F>gqmGvW@qT zn&X249`+qpK@C!uF$76FEfH+Yrz&IDAH08kRstm7f^o!KQnh!*ZA&O@z_^++9) z-#makvnYSuB_MN2IEefZ5Euvr4gL5s+**)%UNMddhA0n`mwpqVL+YF{hs^K3J&2W+ zM?Jjz{)nwqe-C!?qgrVRA z$?3LCbmS}`hjLJ@lquVI&?(3z5`GJE5a;{{m&-mAPq?ZCxFR zd)D?9Zhn1zety}tuAr$p2L!Up?xroGu?w*8F*enT2v1n)h|=YdTUl@#h=+ghkbqspFvYqAe%A8~L-5I4jYD3ra+U zDDdaj+?vwm{>1r6P>TJZr1`1pYMVuWu<%#h7A9_H*2RsLwI$EGruM>`eD6!KfM+*1 zP9B|?fdb;5i``=&+RGYCNBW%TwKSbM!S`qIoT=j1*6r)m=ARH709IRlcRf?n=Y6Cl zx;lKjo&DR{T~PW3h_>fsq%NgzVF+TeyV`09S5?lA#Yh1hfJbpg0FDQ_7nR#+cQK9< zZEFJ!$)A>TF47)SW=2k+Ec3%l~B$Z~R0w2>neP!){zU|dq{p~?c0xI8ZMYTYBZaXvOi^;>@v88~%78g8`z$wrNn-n?IV!-(<1A7+=5T=Jc6v z!0-^Meiwf2GmrPwKdH;?ni@)JT9KkL!%tI3CE|CzJ!Co!S&nG+W)RONn^^rtfh|p6NH~ zg@&khS*9CQB)sgDW!|wHa{y*fyYunuWtdJYz}nK%+KL-JCTOIvvgT6%wmUVy|BP@A zaL}(R+CiAkN=wAW+73D{TB@&}*C>V9H&)fv{>ntu~;a#lTQ^UtSmj z^g$1qfDOMH_( zs>@Oyj*aREfx#(nd0HmG-!?Zl>vT>xH|Iu@7&0$Fc9_fe=j)87qw1W@$i(K*?$h|j zOMy|(`B&=hg()_J5h69fC$8G;Ux}1nu8uZ)L^&I7SLlNv{wd;%;+v>ttH;g$U02!L zL5pk_@1jae3nNR&Z3DYPf`(*Rl<-ZIJ7;rP5A@HxGzk?;7$7SYK9A6@G?oSxGaMyqQK#%lb)t z?;*KqW~zHRv%90-k&obKwb?&=nvoDuJ}}{^4fa-fvF`ODvCZq@WMu)iv*YcOzrahc zu_3RZzLg{{k-r3A$rA&T=)L-d=-5N;;WlEloaxEkj|^klR>~}O8FbFA#u?JLiiqcStt{0i3J z;`=K9?9@P9eN0SzY-#q3eyB+tpZMdO%F|;25bANY#!62&%pnTdGu2)5&AX$q*=Jeo zAn(>k3#>7!`SRsr5ZJ{0SsS;lbo{RK3%Kw%h>_{@rU%Uu+8q{z|+qcL#8*sjGHBnD2%Axv^KX%G!K# z1?6r>{&r(O@h}6QcX2Rd-(lo!xG_X{sc*7oW`0IQkT>K6oSNbD1^RphUM?SZjceXb zBKiU|&E8@0lRe_{JQmbUM~wF%-PIn!v+6x0w`w(`;&>WPaTM9=9Z(}?N+&jnzn>8M zRLWf1TeyJXzG^R>LcWTGdvd)RpFVRv;wS_}X`=dNrOIopiJuU44U__3_lhp(kl#N5 zP=RxZzg`Jm-^Xbj_FV8nklK5fAs*aIdz|h&$2?cj9#tORA+*5`_nrUd z{P%pIgTcCF*pHuO-Eu@-0wTE!W*=Muq2G~D-HxfPC&X6TJ0RNyChZ=9eU~Qh9!`(G z68uuY4m|#8pvM;QD#$xl_6Vrl1H5<$WGLRPz15!yQ|Dj4uW|rB23$1G(7SdA@d4P| zlpI&<4=PXi*Dj_BqrkSj6i|S)eEZzk{CwNpESX-PUN-=qk9T8bI4y(x<2;4@Ngf0b zpMzwnGJ?cB*gy`b9*hP{hK&`yB0|oSC*%jW?h$@JFM$KXG&3pAbG$>-@=DslZ$GKt zZnOP(PI$RqQtc)btHAMZRASl4zJta zV!Y>@>*g4;ZoM;^6Cwhb%F8QQUobGFPBk$Z`I{(-Pv^Hini@ zhC?W7W8cBc_6hR#3gPw$-F6G#cA2!8mN~}1mb-V;W!`$8r(*yw8uP|wwN2lGTGy_M zlGGGWJ-FP!-ST+eFC#2ni<0;acGeHESLskaCP=&r@Gfqrsa9{HTVAE;InKYbZ!>T< zJpzZU>^q7$nq^(HfV&*cUJ6?tp6@g=;O$dyV_7U8%wlRL@OCeu1Bv@8nP2rkZJ+`T z9ycE3Klyl!UAJ}YqA#X-04*PnAA=PIdSvM?q5ZOO3Em5*igc_VM;~4wx?~Rlh*^vH z?awbB+vl9!>(&n}r^vpY6O%wZ{wemMC4A@pb_N|Ql6`Cx{$i=!RYGCSYpQb;@Xdos z;(!VHqz|vmTu!iJONHo8u(BGEk0cekqjV${F)R(Ot~X!o!crkS{gg2Q)k;i{V!>vZ zqjaTc3yhE9^>7La1QuvJXG^;-ZtKL96Xdq^}$#7AJmH^ ze~a`g!qnGXMJ92-$7$!(cIwvG7m*w zPTdAI|A~x-a+ z>Ikngy(XeaN*+kus{%9q$-Prrn{i@;fV{uHberJ&9LzATZU#cQMR-ND4Xcz!tGgeb z_J*N*cezfv(A|s+>gN-kW7shlfp&K=7jU%tGw*V{1kN(+(T(q_@fqm&M4OPgdbu3O$S)Wl9? zBX4!r%0BOQXgfhkv#B&ERI{PG_s@l(KJpp(gx4X2JA_wVuurl5cpz?tkw4Rowt&papz4}2d{4$pgx5C}hvMeUUTcGxiabhBxCde-_wTU9ttw)$L?#K(6B)wRjb z({pR!aNv}@bUwBe=EEI|U#^>t%iDVoRj-^h=3Hg6Pq}JFKNonj`J}=HKlyETNd~lp zlat%c-ZeyKaYC|WLYxAyvllNo5C2;ofg7x)D*}ya+KS*LN}}l{s};X<>l+f(-b7Xh z$%=9V6GLZ0djn*ssZi8(77_9JiMd5F%vTSxyrY7l0r~i>WRJ{jqHQE$mBXv)>)GiXkWSDp3P`NtByjDAQTJSufOOT z8-1(GuG~z^N_GW(v4=*#*H^r6U41cT@y;cJdW#Cq(H6LeLV(Ub(AI84yp7BA<6Ed; zRY=-5(wn0Sw5{LU6Hk@hJ~;TioHJkumaA8P?N`9V((${L=o!i%IR8p$EWf(q&)nx1 zges9M2zGzDjOdTHMv%s@Nn*SDYGZT}CndKG=8JJC?;$SUYml6o$oS2j(o*WQw0^`=Q ztbSZJf?zjod>O+EyLN7~j7cTU&$~|w8uN+{qF6i_s$Y3oIPc->xs!&^7YiDp{%C--o(ZptlQKOL8!ShbjxZ{d*glq5EQ)?u z#hUZ=cb}Y`m>9W5WN_RkCT1kQ`u5C|jI;7FE&V6OUZ2-xNq@;`ZbHIFC^&w2g9Gj{j#OMlKrP+@S(;8cXJFB$ytY<~ zWPZt|Jg*IT5&uLYy!tZ(6**QD>eWNebP#R{?-P~ek1wTJm`|LfO5)c|kY2_L8hWOp zjzqnjG^M5>DmbX}Q6m|IH&d+b3ZPL|Q%g8QLpM=^ zPviU3AfJLW7Im+rDUk6kI!o+@@eyons^Nk>B&VSH2t3Y^e zgF5_lZyNY38=J}OC^%GPlwGLB3l5_Pf{ckukHweEr-i0$BOL?1eh3m9TN}HF%iCDjmqCN}b`WnBf(Zc7PWiUK zLw9#=?_}rTNO1{*If7W+$*!~yysmpg%JG0@ZOEI8RcK9*ujKMjoy+s%g4?^N+mcL| zx3|~Vu?W4bt(#FnE%@0Ojy(7fRzd+$DY6Mb%EP7GuTSK_WUpmhFjoyQ+Ak3IDClBT zMn*RqCpYm!I7U*>7BKsT25VyuNu60gwd<>A^mI2LIEc z#aHLyadU%(4e=iB>RKNkAIF}ggKw#h0fz+jMWYYi0f174=d4X5sL~Kw4#DDEO@#fUs@6{dqIO#S9fv77W@vgQUs7w3XyCRdH zOvk`n&zA&*?j}cpV6wBbV_}h(7qT=k7Fd+K2b(B(ADMy#Ac7%#vvlYnS}RGVxq1s_ zTL}ny`3HhB&i37(XIGfb;W4dOsvN7;MJQHZygn zaTWo$1{XlhhWQSK=~t_!1_y}7sZ{ZSL8TLAFZVpA0t6K(UW_3^EVuul}ErZl6dRu|Xs z%mmk0$$*O-suHz_@$s<<-ieLxMD2MJR0}` z=OUHLZCI|?1VW(E={4|YM=S%>lcE0u)j`4PLC22lIReD%lan`=SJD4ACHeCN@D%P9 z)!>X>>>tNv|1`~IW&C%L*?$Gie+A8d1{ZBXa-<{6v%Lr=-`4-g1){`B67Z;b0fL6UMkOS=8xug8X#d0*O$!N%hrik zZOvrv)}ox+97?^O0&rx#sX8$>;V+?_A$Wb-&>!#I2xkmDv>hrZ6c2z#VL)Lw4f(s7 z(o?%x@V6sXHR&easmyHdk|8~s>FHtF9Msp4O>wU$nXzc-b$Nv%HpSn`%t07b3XjL# zWmO@m*i`E*LM9#zXL)lE2MLIo{N=AO*+xrcXsxm8A#?&}CMISla0O*CKj;g~%L_!b z!5_uN;k^_b1ud=R7cuLwnNE(6k1`AkDr3;+r)Os7`nES=UrjoD@Dbj{Pm$7XFzN-Q zN1tI}4!#F|Ca()q%9$I-J3P_1;M-(zCUy3Ci(Zk(Qfn_1kl3-QMty~=TtKZLM{z2!x);GQN^DcQ}UXdnT6H;DlezKf(|CS zgy1<-5`6fY?FAdOxjcx!D%uHChZTHgzzpiTi7CmLCR7Fb`SG>45I&aE(!mjRWzEe@ zJ)ymHO&#?LOZjZ6al>hB)>r+muTB{9KHa5FYH4h2ZZ^r*(A3o2wQZ`6BY!+IxA;Xn zHWsW2q>bmY3v5OT=la}?OW-5FzAwI_^z-77A_?-b^v^y(12#xuB;zO&XKSZfb=kGI#?H^^BAFE3D6 zQ=8bzN*2|LLm397hFpz^P&rkjbyzNhJ&GW+PS!Iv|7j_)I!)fV`itdY;{Nw#*qaB?HSx7XK)HdR^RG4Lqv?J_IG^(T0>A$ zRHc`|_wVAyD$c^f1_}zlKT%UtGn0|=g1p^_5QlAiYipgBM*1d}lKqEzS-CwQn(YsT zc7Jn$TSCd3pAw{Zo}Ha|fzNKm$W2w-*j&Mf6jQK;&5Mf%M}>iOl>d@ZB9S;MH|34? z4+{w_{2aR?Ih0fet0;p?;u?-=sPxjYHWpT}*KpVS0ZIxg6^cnrj2z~4-8%Zs8Xy#t z8tcx)L`u)j&CJXCxpQJ=r!_3Dt(-0ABugZyi=OW@mmPH4Of))`wJb5PGrDTP@Zg){0eU+Af+TQ#DylHxLA5>wN0 z#ZKS(i7ag?xl1B@6u9kU_jbR;Y5)n?sXG5~f&7HX^|l!b3yt=6u`scd5)-sbe*J28 zty6nzR#Q}TDadX8oquTSspS~xyV&Cg>COkgHcUXc|BL>~SDLz7OjTbH^rHF;vEw;? zf<*c#3-gWk_HYv86&19~D@w{+l(e-sCMT^mGrDi??iP+s&AiGgnty1iDReGk=#dH0 zgRL!fVUJCH*S-{CwjZ>;rXZsl8Y@2|*gFS9jf#|2GT7h8Nk~*yT9}>VU|lyhwFC)* zE#zy})YU;_m$Z+S3)*GS?y6UYRYes2qJhlzZun9x`ih6>5iug3lEJH^)ANGo`y!k0 zLq|(V2vVJz>Ko2^O(6N@4k(%_snn9NT_MWRVg2;*v8@~=!`xCEo1-xlfo@6(&F9R1 zac<-uu9AvC8rO@HJnp_fFbQ1^*02F7PLUBHoke9lNN~6de4eV-nAz8mYZUvW%O(_LKy+;;zzesH}jo0&gc` z=t!CAF@D+#ceCZgEr_p!go2V2I7g$VcX{5K(Y}7J4w{)sFPxOpNFl=Wv6Br>jEt%N z%tIn{v-i^U?CbJ+4v!=vYxrI56iCyBX7jxuJ3qJip4-OhVXcXWm6w^>)hqnihr3Uy z5x{M0Wo(^YdEjW|CZ+EoZK$fN_0v?(9@pK*=6Bv?Nt^f~nLYNPZ)EVGCa*hWj~+VK z(o-MSoI}JINHFRF>DNQ9kJ?W~B$Hb>ORKiC)%)mI4T=+JdE;x~UQYFnoMnmqqjYRr zOFu(v&hg2NjHf3tsyW0@?X<;qF>sgMFUYm^Zm_q%Tg7YT;0DEF-x<691l<7`Y#{mN zo3X8)yE!P>sPrNWt-N^(|~7c&PB-<|dAds~-h;QLC#_Uo@%D1Vtk^|Z9l z4qT4r7Y`z2MdP;Pi>n_a`bHQynfckuvh!>9UF`-`# z)3P|3qtdd{B=%5j2Ki8kYaxLm=T|e zsC=B}b#YAC$N>uLzH#=nG1*(XnVXx#DNZ`Zr1N&jtfXZ?f7$hm$$39dE03s4D1cV| zVq?sZSuxBo?FJJ#@QG-Q-=+EY^=scqU7d2J+=(ex z`Q`E4@^aXm5lz?)l1J5rqnMqCgNvM{nx4He*!^dFK_}9whm*rbH~{&{V^g56)s_REf* zxibl9UtJ?%6%<@*VL$8+ZhUE7`Y>ITH#P?!XK6c?yO0mh&Kf?JoSku~tK*-TF=WyK zJb2%%&7HH;0zA2RDEY~Pn-SMrk`G_lk}mKu`KGiYe-fdnA`GkSM!VbY!~0ZPnftgJ z86C8JGBr9_TWPSWpVw}4Z*OIMFm|`EiW2Jh5nm=Ajv(lyn>&Ne2iDY{^W`LDl9!*Y zrK&YhgrA%L?eU9`Rl5t~;o2H%anzr4qwDnWHr{ep9-+*TMK?Yn6S`YPRDPVtJ(D_NSKn^HjMftb=hhIl)Rmuq3zm!?`rpQ_M$2DPBBX? zMz?pPV)F3T(jflUhu|)F*p^X_JZA`%y4h83_hi{rsJIYnoUEvLTv@>wq> z@5{$D)sZSe2LFDI(LshXOXIU+{AQqaAer_CnYFdo)Ae2%48n-}6Vm;|MLmZ$xqUJ} zgl4KKO_(hvr|8=vHX9q%(~-xJ&4;7IEvUMfEocr#UTpk$){Z$;)qQLwC2gzrCBf~@ zcX|E0yODzl@r;1>M*~;ATgwUQU@hOsNV?G4LF}U9_Q}Zy?)N1$NohG_I?kXUH4MtL z>&6{?odz!Vv1CB7%Sy>}tK$S)tbCaqt6$~x6BFMek?$PpXSM0Zq>5^TQxTVQYjQ5W z(*L$Y#D`@SnO8IRbF}w!e;y(;@Y%D5-aIA^%X+_&&3`w+DeTbFR81zouBZ<@o|eLs zV6qS89{M#o8IUI)y4pM4-__@*)qu$*lCY3!b-zlO;k{o5x-BjSuYq6RE59Zszv52< zyvQ_@59Lt` z=u^C-%kcO*JBIg!VnR;_AmiXG)HB*8HVN}3AoMU05FrZU!0~w{Ak2j&9T4mtoVk{) zn3tEQu(iJ_Zve!Vb-h7qO1z&Vb2kgDuX*Ox=aSH*#EclSbB3bGobLxD-3X&`rEYsg zm%&y@zERo!7Gbr=;)*15Rp*6pan&+$fYic_dD4z9L#z-6BH9v&C|}l#2OWC(X?R$J zzhz0ttlU~rEeP+@-gSaWoZ@8RhKU4vdiTg!p^5D=V*_ zaEoIk|1L)JkSg~#}gBL;2aV!Odg+DXukEJ7Vc+ORE0rX3Ts~#7Eg^r+cvAwj@3%ZiOFm3?VT!Y zk%exA9x7M7(F27mq?LgeIEUd<1tbH8GQu*I2dNZAbdo6-aJww1HW??z6=k%eGKL*k zZ;e71R)|ype`#L3^QSI{t$J(qM!H;LyC(^IYeI^5sdaT{tC;V?tYa2i^}+VhxVE4_ zqlX3;HqotO$Dp8pwM1+X5ki|{Z3tK)n-g;xTc910FztetQDCA;+ky5gM=YLQ8 zKVEQq#AadGG3v`)O)jwhqErP337`+&lVf5}Mz#n0y}jr8l?P&g$NTd^gH^7V1zXTY ze0{51ZFPM!D}ay5wV^1lq~hAv(Nse?x-w{Wy}$c$RW*a9NQU6$E~%qJe?T@9rYWbS zAR{jgXAmQ#HK)AH%Gli2Qg^Sd1OMNdoYr)Ivm1spb%=Anx$7+Rt7@Sr!;wER6Pa%FFZSMW&3Q z2?k7gy}IR;@xpR(&Yo~-3YP);#jbE8?1rvxmp&)TxLT!TXlr@YHNCMksa4Z-DehV| z%xm+j8|*fCS^fILF!(K!+}0?iZV7y7B1Hwg31k5BC2J_IX>Y1^x_s}!^Lbr67vp|| zR$nwCmczqpn7%>1fWB@L+j%%I9@OulcShWWnNG=?;zG2=hs8mS9-y%erLJV#IBnwL zW&)`Zo9nBQuK|lA+flOozAU*x(AToU@6Ub1GfgG1J#P)mAH8TMCK_=eZ8@k?5>l#t zD)JA6EzNazNFFYcLxW|<=u3Z@9fSNY|Bb!33X5y+_BC;LcXxLW?ydoXJHg%E-3jgv z!GgO(;YDzFmq2iWoLcMK-KYEP?|J&{i`^IJSydMlQ~@QkX8q?F8M6f*o}#^-KiGKrc@~xzmsAaW=qc%GxJ1c#Ie?1-Js&8as;Jtm z4xH&%GZ<|go3~+}OU7bnR~ADIac&}J&VZRBB8B>gh`w4Iqa&u?0TBQ}y|emnM`g6q zkuY8z{A;?|_@|^{`oD4;JK`0uu0XMnoLpU1!}%4Dn!rp0=D*hlt0T21h*fn(;(G-m z9Mj>;uKLjqJVrGP<4Ou~2se*z?#(S++&`C-0k2QvoNGQXqn1k?$G2O!;$|$C2N_zt zsuC5I_PjdYxcgq$I+`6LzRPh; z?%B}DX0ohG?{bK}Hn$j?Kw%T>u5{V`C3&j2sSqE%QV3{~lmo4_ZM@~pk0nG#d z=xptMKDwOw^Vrozs`{kE9rO9Aq3ch;@#e$QC?k;s*7GL-#Iyoy8Ldg^4-Xdo!pDx6 z8o(To*{-q!Dq_qe_M4VO##OMdSt_Rr4!pma?Vro_JmMYOLJ$tD$NvvZbRTG;8)Rnf zhW4qk@j+g#E`*1!1;^(2k5OUZ^C2F~$9LxJCT51$J#~HENpVvAg6urnJMHrvJ-zk9 zWl4L-zSXrg9V6cxJN!B!BEoXOdClZaj+D^bnmFOY)Ct%Dt*G*Jcxz=wpp#|@MMXny zc!1T(`)~<=p-3S3!+pHcPK^;LVM};kzie1n$HXj@pYO-!(w0F{&(x9TtX9?$9$oY! z@7KLVvQv0@Ym`w(TCqgNt2#mtNYCSCw>Ni@TA^SZ*wOlM#%*IzTa0bk^F`0M@x{)< zIUu1Q_K!moAQm_+=gQmd$UdzrxSjEOfd7&n8-2y{Dw?4uS@f~Bh`2~wZb>rB%2F%j z+uPas0&%aNR}9@SbDMrR5>AWcSK0T+l97Uy8Bk2x{_2F1d?c>1@#h$4B9|MtyF(OI z?O`>j(uJl@=U&bcbht-G+9m~&ll_JI8rs{tyNk2F7=H1S-}qaT?x0`8!aAi? zvCMoY2}_t;Sjzupa?2n&nNiBB^9d?qL|<{1nW?7Q9%O_6591Oj>G$|F{`xvNsL^6; zVgefK0||2O7KU#AJi`0|-_rc}`!G|JsPKe|3UP+qM+55FP@7^RH#@eqFm@{I!TPTQ zVCpAz*t@!X!O%zPu~FIzlD}cmUyDJ3!3t6(C!}27)e#Bdjlznvm2ikyO9&Rq+kP6Ui zY|}UN$C1l#u6rp;4Fy7993M%2kaS&4PRjlA{-`yf?y6VNGcrO(TKZR_+9%bD6@*6_ znJZ*ul)L+lD?7)pP+tG49B?sJ09N14f5;}2T6NM%jMX@RDjI$+(#m4a-daljl#Sb0 zr{|=2ctk|v$Tix{!#kV7g8n_5Z363hId#ig8hg4ir8-h+9VVO~AFjqmI%-uggN+ovP(($i}Hm4`2sjQ-&)OWzB%%|9@ zQ(b;<=ve=xXokkPwXs2OqN_?K=Q4H8VZQO2@>&z(P;2)g><<6B~oTiVI3+n_HEZ z(S!<8Z~?Z*Li@@2QOFH)#hXQ_DXcn)M56N(ln@qm&s=!__lupw#5?DgyJz2_v^&L< ze|e(J*@7piaY_G%ejAYuzuG+nZ9_x^=-9-#1(|8Nxhd7ErDbI$90`VA0?bN9s-@*r zMv972vGEB=ux$lxW8S#5zIxx<-*t)C@?oR(!vnH4wTsvJ`8wLYz5cuy!6CpSC&tAj z#K*>}$VyUI={8G?i~H%@DY}_U>R3CG{eE!sOc$K6`!0(O>9ZKZ7^Sa{lhl-f`#T&s z?ry7>Rl>Q_j0DfUPY(^Q z5f`B@oaf;NmFSviJv>oa9gRdjf)IoM<%KRf)Be|pu`yVsKge=9(-op;X&dih?WSDd zK}$g4DmCF^*{SX>KF0IO3Td@AYE$lN&tnVnZcF&@(c*XTvy8sJf&$h;vCFF?22T(3 zjU&rZ3MV3QSSP6txm6bGW%t>$0!|JdZrHBcxShi~2Z!U$PavQ3r=IR>>IHhHox{`U zmW|o2j!rf^K|)&vL{3a~eH9tKSPxp)L$YMVw?jbzb6zqJPSA*O|0L001cyTx&~&jM z^!?-_HsG(eLrG}Q)XdW8#80A)h$*Z~&e92!{#6=kH#-f>xZZvThu{NyyT6+pe&7tW zE%G`;;8Pq~dGYZr*n2$G4hTdsB{f7S`X{VN#D6qneUMg=2x}j8Wf1-es3nSGG}oI<_P992&&D2x<4bHs%@a|4}20nmzu_g zomC1Q)|1J=4L05%=O4rqNu07Icm1#;{*ZpKe>arCkZrJeFmv$(u$-u0*hgn}((gKb zh7*4-%fj29o}N+hh!m>sqK$KtrJqz@bFW_%HEJ`Z<~+t&!uJIsB>toDFr85PlGRa zas>3$b=M*nuDw>}9pHKyZ#VN}Rev&mP1+=+)erw)?LP4Q{*N)JcVt5EX4`)=BFyi_0OumP-|=IRgPU-~^~bfI`~7}PvfUtMPSqjeXYr7hn-9NTTR7EzA5cKCtuy`H{- z+fzX$4f@!gm(_b2Mm80rqVd55klZjfNDFwC?AL)I!Fp{07@2MA$1;QRVmAfvXo~M$ zAB+O;xQ+xV{$}FO+ybu~@5@RQKOIjSU)K}B5R)8`lKa`Yf?U=~{lpa$N(mt(`50)1 zT*gqG>8T{#D1(HE#6a`*fyJ)J38EMYPk$W1k|-msp{6#w9xpa}5Usl*EN0MZXmHYN%y`cfNP6)JKA45?^g z@hw3^n>Mo_16|B|5%yqLG#VMXaba0xu&N%GMpQik3GH3-K20D(Lhh57&ZA%<7njht zdEm7y89F+hdPH>;ZHR;m7IK4-_siWW=j-j=`2JX!CTzmhX2}%Yk81Ei+}?2G`(Nvx zlAi~aAp3=E?d($KSDuiPqGZE7+B$-r|0aKKbvC7DuN$?Q0aqxM# zbB45#uB9ooX)n_84DeNb6dVWVt-T~RFxcGu)YH=gvLUl}3J9)!(aRQ7O9xLMBIn;J zKbP`!Gy-Xpl>?1f$-vR+eSTic;&J5FHKgakRy=#_$sEV#gF;z@J4v|%5M=J2?yYUD zU_djl5Or-c;uC3;T8s<#j zjwjslduSw(cCU zfRLDk;N;x92uZCfu*=A#?__u5Ink;Dr04xXfJa0Eon3XBrOVGbpNd<a?|RN)ehq zfI4u-c!G;PQxEWj$h90iB?RNAF3ifyG1Af3*9SFva;S^vb_d=K!J!yyxX40>3d7NC zIw_sVY8r3GPj!QIzZmN3>KYn;(a+4uqL6|Yg{#9hJK^9fF~igV*<%$ZYiexI`D|+# zCu465tv4WxNvrEIMX{Y1G6w0Df^tVJf>t*+23I$9jP!KBDfB$iHTjI zqHT7*)OlCL$(r*Vu%@=YK&OVjzP7HO7VsDruu^G{djR@S$K(1?=L<~meANXQig^o2 zfZ7%4QtJ-iz{iIch!Ay9x8NHTnuP9ktTv)WLOz{*bF+urJzef9H0trRULpV6u&d9@jRXM@ z#Cmb$1JVaFDc6*y%oC@16MrX^97W!W2h`UWJp3Z z^<7kWeT?3E%c?8mk#EPMd63;IZYfjD-Al*DGB1t==k zM=6)w_;a9BdEGr&?Wu2b>Pt%FeMPlDKi58)SzX;08cd>vb2ekm#PdUgcxzfY_-cZG zBYPr_tys&5lhG;8$)aTm8yQ5#PVlLxI6L#1tbPtl$J|8s##&3+k26)AmBoFUhJ}-5 z;1={0!AWcviZwsWmRMELGJTilsKp&#Z)?)VaqJL^Yk^cFo#u?$PsNbkMKtOyZk)%i zZU>7Dl0}2E^h!WAMKs$g8t>^!uu#;O@<;2M;xF+KwOTo*p#N@=msmMlz*X1xp}ePt zCkxyd2k2M-*|-*vN1CK^(H~t?#BLD-=DW@^XkS?w1BR*Q;1ypoU)j)C>Fj#qOR2w--V2z$)vt{Tdr_@GMDA_{7dnVoBGvD}onzC-5?l!*f9JNiU-P#Laj@gA02D;Zk!X5k znWedB0%d2^5W27)b={IPU_XG<@+!jzRD4 z$#|h~9~IosSV2miG%#|5#V|1`P zFAtGF&B2j!0SsWNLk$n!2B9p;hkmnLiV$opK9hZ330FPwCW`6v{=F!gH0;&ILMYaq zFAy9Ziv#WAmHA4hcjzBEzFS=5+)mi7b?z!0o=S?q)ENvNqPn4qm>NfdU{1c+J&Wrc z!BCeDtq)7xEa>BRsNK}4ctj0T9;BMK>;n;+serK?LdJGr9#>^pltnz)ZGHKT@Mou= z&51;Ksj#;ov|{LjoQ#|s?<8|7C*V{oI0zP(O(_^E(Tj8^`TX?k=SAA)w-(7!0j>fL zqTIMUs1T?m^IDb(N`>4^51d&%R)ks@m;lW@W{+B?8Rg$Un6j#8kq*NjwU11*cB#O! zQa#k%t=;@$JlQrfRSapbbLmNZNUbg@ShN+BJR$v0)%f_Yfy0I3*|5|T$@>N0!sysnqdzPB0{->0wLR_*19pMmc`SwV=*$ zl}%m+pdu~AFtmNZ&qCJlD{f&@!IX3_Q8Ku%k=9%xXR6MAqgXGCgzR1*cs3FO=ssGIxEnyUO{G=MPjC3vPPsh?h8~dOy z^%(^%3?>>nSSrc$FD?bUlr_b?E*`ma=l9^+Yc*MH{Y4kU6h?GJxXd7|e z7#J8K)9Y^Y+;6*Tk4+sBJ19b`R)*`kOKi?kMsEnD2qcYc3uRGOL0&l6iF}gqcr1S8 z>tG~XZpK#{!xHW^B%0`Cb+$htn<~Zyg_!LPKJ`n$N=8iY9TG%#Y}Ll(Qq=f%~e z`-%TL%#Si2_B8Y9IOp(KU#Oc&y z$cu}#X4kn_Af|$%KJN9zJ!v_Q;amj)6_Mq5b$NQ{=DB;h;NXq%Y3PGq$VV*xm!QbE z0xXg(l}-QaNWeLS(BY%!94Y;WrdD8pjZnA=ZGG&s^C8_6X;p$2UjV#_H4@NAyt)^C zS+AOv)o9!6F4kH z#=D4KK(+}_OKsA9%*>^8znkJN5{(U>1k60=+`(o0TR&XzVGSS15HGXS^DswWxDRj1ryLe12@_XZ;fYr~x zrF$z!&oTsNV#+j62e)f&O#lcp{D`Ps=Q~M~5Jl?$zIUsvABf^F`#YNg`Xo0fEqB3p z;RUG}D}fEAf9IYNIi9%9a9e+Rw5S29 zoC6@&!M`sbA^h4*AR&x4gH0-lte-sdFQry5=<@%F;PuixxgUv!i2ZZ?yhAL1!7hDXXS`Zb#QUR4?91=;I4QSL55Tgk(}B+DH;~QU$CanEG1DC6&$P zp1F)fGnUKOE+@-QT7vkhKQs~g- z07BbL(LSY(SyJ01N;*ao^bBAqy!2dtd+6e0WRsrw^rm~PdQl4{7&43BPbr1|C`Ig5 zG}NC7xkK@3xHrSd$-yMew~<$*+e(80eYa2^W?3vw z$h}eJ==qXu&Y5sGN(fVvzZFMlPZ~Wve}y;xB?s7{ss9&|HwYX&=S$kg(~hOf=}-&fJr4 z7MUMRx!hF?EH^Ss8s9Ms5Z^NkH4@0(%YK{c)buwMI!25u`Bq1+PNVsI|7M7z=Z(m; zS*}LYw|-0_k&Z<6+FE1$x1Wot^iPiX-}uDEyq+DOtxc`x$?D#8BP@z$lkpJSBhvp#cXUnVeSHrOXEEeb#i1nBh_a@r~Inv=vP~ zmPqEK@>HnP7P(A6S~$)lk^54Xu!3(pSG`O7_}b9))@h*WTXJymB3)QVQnstG)U`2b zkP6S#$4Rag*XBWIy!UERN6yC$@|a10m1K(Kwp97$8U7eC@OJ+ySgm4vUQ|BZyO&yzY?1ni@PhpAaaIugo<++8=z6<471}xt(~E?@ zRa&AL$ScQ?SBM8#N0@2r7xhzvkFl2A6Lwxw^y3d2Qd}rk0#XRkum&SgzPP{)LDYb+ z4ZtMqWA+tD;+EUd2|A>Q#u@6Y<=W0LP(+eqB&rBeh<-jMlQ`EC^wr74SZZ_9n}GRz z<5pFESf_o{#$@saMoT@PcrOHME|2{C|q)-)$9T-!GT7OWp!fT8$(Ns<5KPXne)6XHBV4u19UjK-iC=W}wv*n}^hu!?lMiKq z#NC)-QXDtR&5TX^X=}I@SV>n@6l8XPvMGzLdvI_9oO7^u8}^0}yHE_hHal>LEC7%x zc}h|D`lIY@QTEn4B0OXFW-B7)=VNZ`xOIUrv~edL6kg)wb=PuE5^?u9{BV_lPVG9` zWW~?W7go3Dt1*bs?nOJ)kdtarAzMg%@ol!&nz;Bv(g6o_GfVOd#e& zKd*RKKQG^N#3``RlesT+He$e=#WLMpTrIv8zrDTnhz@OLIpkCnT%{eiDfIfie`vu( zts6H6U^7ZL-ZaDh(SCc;N(x1j-*k@U`CU=54J#}9Qau0|&Z%+`Bh_cnedDX?ttb0r68@$Bl zsFRLuXN+y8Nm@BkakbI=*>o2n`ZRE~f&h&3#tvdZ!^Aw}3VEkJ067OG#YwUeX+rwZ zhPA`_5M#PYscf_ewU~`Xwd9U08TE3FnI|9jYI?^zryuvPr~$)1?|4ZP2LXNqZ=^T-h(crzoKN@x-JxI@wK$Rp;~!v1@8+<7OfVJu zs;edjW}f2t2os%P5}X1q5@FRMkkHBKY4!%k+NB!0WB?ub9#a>eWS~UJ5TyF)vU5U9 z5&*-}9UYLSaoL3Gd4-Y~LU7R`w=x02A29*@)BY!YzGtC15~}Zc>Z$=q?1v3Ded=a~ z|4Z5VBRu#60#_VH_TSdz|DAA=i|rrb;y=R0e}s$w2p9hmF8(82{71O>k8trH;o?8S z#eam0{|FcV5ib5CT>L*wxXAhU!R~*azx%HU7uoqizP|t9lkkB&h5yY{Y5xy(AA-pL zI)Dd*gvVyw9|R8hU$_wb(GMo+U0n5VD+K=@-|~Nsx{;IpFL~jA-T?k9>PC*gUiJUj z0NAz0S_A<9FM~ z+ABw}A*fAX&%Ra8`%sfRwh;*}_rOmEj&IkY6Pi%}?%bQ+pD6Q?%CVZfy#4aV@cUyG z_akg{cEYs4Yq}Rzs@Ej^9o3(EcM>z+2%t=#%APpxzS~vkC|mSP|0qzPcreL{q+)!A zE;hMoaP}s{h|24ofe0R_SllSrEBDaj3NRBppYV!FB=llNJ;Ti_#b&ZL$ofy~@;e6NxNC)tEe_FSS2HSlv`itxB%6(Sj?i0f%doe!2z+}nIC5Y&cxV;C)MWHt zg2onw8yrheX&nUbWo>JFesXehc4mm)GazR`Ow9Ma7Fs+*R3^xkh#p~qMpr@LZ9kMZdSnRFF$L9(C3pmYa}cy9lIZX- zi2uB?5t3C_8UM%H!6Bj`5B>@7#E6;?E;sfJmxU-Ye$U!^ES4G``)C6i3hCHIYx^%~ zG?pyW-wHvHLolr$kFTz-N-e;^=H=z?)P(jBgXx08<8Dakd;)7sc9m;>hy)YV zBjgQjLyzfRzYK;!RMS0p6)d%UQqo}RqC+PJv1GO&VrFk>rd&Ly=Y zgGWTM1Pnt)EVD+vE)Yl@avFbP@`arne>A#+Ve@*Ts<1kek{TI_hy>o`6cp6gCnmO4 zR0v-4@vp6ehB0$Orm^!dt+A$P5%8IotyFy%Sxu!ni$S?ed3~ZhgH0hJf4|pPNQlso z5D9Vd^Z{}4kkAl_w>N+PZqc!k@c|S&#kjkBH~NwV)yPJ1^lVm%gIjGcfa(o29bPwSPsR)9dL8He70Wn6|9k>Ig)%!Oloa&(PNV{5g7?fstWiQeGhj z;nuZ@b3O$#N~y5EA_udG9XN#Gt``3rUP?z9*%A?Ri5d&vezOLP9g2pGN(hvF;pP;q zZ)k7s{!!go-dfk&+gnsrq&5Zfadv)gV{22Vt(BIJb$HCilMK4AhN5N`4sj!T8llIj z8--$^I~Sf*L{Va8ZEA7}#s+$aHNw;5uRcprA8IYO+#@UTI`I4*9*W-h7t+R&hkI#x z5lq=xjAdn5$^h}f${Hk9thZJOk)9cx7C+)b&0++BI+i?UplCRF=$RSo(e!Q3avZqY zT3b9ft}ZT8blRR2_r7Q!UnV5z*)vZoA-gZ8TZ}yO?TYT7S=*Y#Fno7ErHE!+%)I>zN>nYO=d>+1CN{qq8A99qot+o`ax9%K(_+W$N` zE!{xRJTa{ZolPd9f(vWwY6b}v55s`GXq*)uks^n|0h6Xsm;X+V4wkB+s;*|Ry|A$~ zw>Zzs#wP?K(I60Fwk5|>dPYRTy!dtruIoASEPYPLJw(s86%)Ri@%UUWEeJ_mk^(*vXMiTgVDr}`b#Npo0u4% zb>nGeRqCp9{|-J-z7%F9l&(A8Gk z+c^BXAjTuXCoIU#&dVPv4EXB0^0qi1BKr>AG2YnWS**X!wNo>bIf_&F($fHbT~%q&RccJAi&u#UFmS7(Y$C2{~l;SbmI?@v9!>qZIOMFC%(z za@`+zIh)vfMH;k(vvyd}N@s@%p5m;nXAS90Xb1=hKZ?O81%PZ4^Eq`W{vti zB<&A&Z;xm5KdlBdw={w#TaoVExR$lk53H!9K_baw7|Of`0kxv!mXd+5TVKC4HFBtN zct1r*(v%*n0o?9T;ktiL8q&3uLn0@Th}*Xsp&kB%@0b+ zPBM=4VB28Vx~5J zG6b)#`olB8!ooVH51^oIbqBni9nRcVB$C$EGyRpPc^X#t`!(rML>XqJV(RtgOz>Xtsa0DN-5CFRzk~JS^j$|umK~^pn-_vc(H*? z4IF1eQ4RXcaXatkH2e>to{Hf`FGQx%_Q!+R*Ne8tzFuEnFs@k=M|EHvGDTjs!tGD% zO%K=Mgd>IrpJqFS?X$+rf#O>K5g-=y%A^Pr4S!dAby170@!i%1M)=f>0t6%rsYt-9 z0X&jJz$+M!6`ujia=96<1fw6Kg0YD^}jfLosNIVH~FTRn6D1#hRocCW(ka0hhjzN#^UOl zZ6&P(?QRoL+*%j3aV%)&3=?r^nbrp*jxwu^#@eRv&QKJCW;&>m<5Oib?q^9QV^*TH<9v zGH}uYqS6pMs7;0dukzG*<_#tIq~DU@$5JtAU9)%wVv+`=6O-kpn7)6nkjeoV^WOfx zuP~RKI>d_?J(^~`u;$C!dLZO3@so0v23oqiEJ`7QLX?*ZHTR%tZm*51VxkV1A%c_iDjvU)9L z#GXGZ*1Q}B=Yyl6w@>{$3W_A)dNoCO);V{k$JQL;7uGy@^hl-QBbOG z_K35UjH|u3;TNGlOjl1)`W`%006(AXv5)c?{3_d2hMZm;HZLV?&gx7xE#ua-nkAaA zf&|$3*!W+eN_*;J{_};bz@Vv$W6lW=1akg@WOsxjz;oPz4Xxm1oUrg^X)5E3eolc0 z!~Anx7*gJ8V=7~Sn#%9fTFMW7y&}$@!2Pckudc>f6?q~IPU0*Z7;1I%GkveC!A{5W zmQ%6-I#hy}3(+|htx}@d*K1blThG8fR{MCY^>fXjkC{xhd=*qp^U~on5oZNSew%9% zacO)Y*wjbO4AG{~+XS-oO$j-h5w%2{Vc#DZ%FQ%k=X3XG#x}!|goxnhT%?v8S%)5k zPvzTA=jjg1t{m+WEFJVpo&b?9dS(YMqA7Te6u|?@a!D7B>^4LOH;peRf?)V}#0~5B zCxS^Eb%Cx=r7fJ zF}UA!zkI<(^}d;P;d{QK&Ab-*d7q`yPThz%t~*k;~i=O zMSAlD1UBMR?&Wmc!bhIrj0H)cNi}`jP+GD{2>$2qMuHJ_B}Ml&fnf;KFkciOA5fE2 zz8mBS1V}krGsBgh5H zj(<@+u42w&m>$q;e(I}O3Yhb3N=^M$HU4p5(}TM*4cdQimB7r8o>O;NzaoSbVq#*d zRi@^JdCTI8TbC-%vYM$=l%zIcajE40>h*hrsYWZt340pu@I;FM%Ot*|qBPf`6vVQQ z@d~B4#lc!YEj2P8&LY8eg}|rW`bI<&iJ`v;bm>C%AHQWh`Ay69r+%A_JXzv~a6FGl zR57eSiD~%a;KcZ`mXP}pS~G}RY5cpc>NiuEt^^u8>ju6esMJ?6H7Q~$P8jm3ruHB2 z*Ebp|1?2ZxyiySak68lw z>5})Hf!r;eLt;XR3Vf71*4!Ku;FQN0HLCVlD|r5UmcC9B=qAw&VrrhgzZ@;`9OPyW zF4jFE3+S`vhfbD6qIG`B?0_9FUayS@0q*PyA|34Iz`vCog`uKaI~QXphDUI5nx=3f zqH!@3_k@=01LHz}VpH|E%0VjfU(g@Sk%6`Lf0v99y7a!@H!rT+aQ zUFlU|qY7-AsqTd|gC1PKBe_ZDy2@}qf}*T!gP`24P^GBTPd=}irTfG7i69kpb4yD{ zqw<6`kbsEf#KF;_9kl38M>5FA2=B`3_%oakp- z>?9+a@kWq+%obo0_FUCO|4U|GJ|1Ld!Z+!Zc?AVyMobl&G<+IYYAu{S*j78vRlXD- z?l#LaYd8W76LV{rD-c|AdWsng1&TqNT#epE&!8tT(83~mm|m{EiPRXx+f(>W{#E`S zmoYU-k&g2R0vq63oHDthlpwBf9KdX94FY~tT@ndjXv{HT9e`o$nMX#)~N!hr( z#d5L(I{Fc9O^rmttOy%MXt@S3akd+&y!7isXEe8F30wT-EjMHbZdg-$8>bB91+jr# zV0TEX-*w`>VOc9^euL^GaRET^Zf%G`717Ai#08Wo{>e*V!=L~X7p9sH zg_5_~qjuPfL1hr^l`53%iXxiJ&ZpTwkTJx|OBDZ{LsG&YZ<6@j+0@nMXl!h4jpre= zAK0Pis8E`ChGl*5P_$v#xl?iv-91)ZEam}*zvN>p{1o2Ou?Ty`UUsE zyqt!Ejf5$KjamW@pMRXH6cdqvM+l1IQrW@qU2M776qE>cVM8eXUR+(?+S}V$1;0;b zJiW*5<+(4dpkQw;@K zih;!&sai{NjaHkRA44dA^_J3Fygwt*sjZ9``d(L8UClIhktA;71*ZhKzHtg3ETo^M z{%m1g7t`Fz`W_5XhFQbK)gy;{D}hI#QV_Ibo6LR_P5(7Z7jIZ2s)&=itjxk9Q;5=( zk&%&^`ES-QV0L<3QRNWQD&)0$6P^7VQk?nsm<@9Q1OnkWnMVeR{gb^bDYKZt64QNa zWpqnk3y=!@;9&5#r0oH5?$r3q#FVVU2vo(&iL6ObYEusydp2GaP265`s$v`xB7tJ` zP%cHp=mZXCu647P>PbsfB_2?2xUxQNK@+JRqob_6yp#$OVKl(xOj`#I`EAgcQ7}7l zwERSlX2gV(QZP$2f_cL(h(t+$p~uG9PI@1>lF9ecUkGftz6MmDWa|8tjk7#+fxySqWyj znomdqD~%cql<8gZ6Eg^E&`;VJk`>#(cVVp}f&2Bx4tAW^p)po|r)<|JyT=5e@%5e- z&)~wu+S1nCI6kBz`}g_z$w|Un0V&6=EknHKvw>qbft6LbPyzOMjFAkfI56n|Ktm+$ zmku?X6|k>J=L;752JA?9PSz*P*T#c2jF#OoTl(xPizE0=j>H#mvElg0Y$H2*%sl0o zRd>wBI_NZ96<;Ho%{4jVp?JWFkmOs7=*J%(P*Aw=bT(i}FL#-y6p4ESK-Dfq3QLKI2i>Vp)d6|HK(Y`WhB%9>v9R3WXQ$ z61@ZbIb9rKk=766dUpRL>Y-@H8=l3^iCg3R2vEZ$m~sGA{II;!oo{!i?;Atkx-D5FwB@pX`ncRjsxt{tX9?I49xhVue2fs%WrMUsJnF)H{q?4p8bChZiyggtJpF z5MzcR0)pqq5`aO8PnbAqK_-Xjs6nRR9=|hW%&z1L_`^7fMqcZyYCw{YMs2BWNGD)m z;FVB>A?nqjvu2)io$m3bh9oe@l5`wCA92 z&|S1>1nyz;E0rAW_^&5%q56}Wy~)%$LHclKPiQKniO0{l@Cmblv_iCC=v0Rs@5p}d z_HV2oGuvVv0dGbDAJz3`6ovox+zHcrCgb7c`M;Y*ob3Po?ZVt=4bA1-$Hgdwkc&S& z7i8yr0F+RUpngCCng@q0c*qGHIJhnpI6BlSu9O-UG*(C#&HdYQ{*^22@G&N=qN31| z+lO%$1LyqPmG-^MRY#K}DL%a@LJ~wN*#F)aKSt;W$8~THb>m2gb>oYV&)@xDh#po2 zd(ZoW3jngYyf#sY_fI z{a-gq@?v9W=YbK8!wW zGQT&x$dzm_4^}YxsNT9A;6r+rixQ(*Sy)(rIx=QvW(v{>D-du&kfPvN5W!%eq|Bm9 zF)HqMeX$CIkKTb;1@v{0VE+t36i7MT=DETl!(oEGV%k5j1=5BtEy2JwL%oBER0as1 z{y}*1D2Cl!f?~`MPscWe1@a5n8(+R#4oifF>x=f08kt(V}s6^?WowlA~#dz<|BFa^T2fWIKs^UDeU0@CSMq z-RvzqV%y^unD+Z1rsnUe(fMF_KcFbjuyEju9-kgwUheOopUX%i1VQxn3JZhw-1#IV zR~%WfO{t+hlB8+E*4?TvR8J3gnP#*;s&@@Xt70Rr$sOKaKE9AHV{ecU5D<`{u1&}| zlr|U{91;}KYa(RW)>b}=Lr_6UDDjdolfMxxsfT{mnMofvXWNff=->M2LZp5B;NW2T zQThRUVdCiW(g7E>hNBUgnx^igtFn>i8;uJrnX4`gS1j zm8Q9uSEV=!23FABxPC4Xn6N9nunymY%LHxuCw)$^PYNe(Gj&(_qJ|Cn&LIClw8m(G zvaBjKO)zyTd_9;;Ql-2sA8~zLEcm-J!@+=PL4{zNu%4|#7^sM z?d^3$Hm!D;l;jA*KBlJJI6ZyUSDX9Qrc+~la|p&DsN7FApDW5hD+<~wjJ@e^$ou4IP^o?{{;pC3wh6LUs!+j2u8Oc}AlT(SYr>{Ty$F6;i@+}=ROO3V1^GVV;%PP3hYuU|W4b^}YMSE36?*!UOORXf{-?&^x! zqQqTsI=mc+?&kd*PHlmmeXF&|-GX}tI-Q@hroh*Nk%5uvSJqb53Dj57G15=XPRgq& z$*W3J`sqAhfLzCAb+I*dy^(}}oYz8sL=NTWrPf5Yi4@{u*2}9|{jFHAt**8`KOUz0}^$#-U?vZLQVf(j0=JA^oj&X{iQO zDUDxKKz8RAHIm!yxVTG`3}Xe%u5UsoJnQU+MyojPhW>zM#+}Tk_Rv@96ZQ7uD$Gbu zHPRdffH-PK_O`!HPA<-1x|Bh?0$DL_D;XKz^~EC^=92pFbRx8nJFzGkurJ=G%Pqr$ zV>3^8Si*K4dKRwMZQ(-_(qbBd zAMDJW04{e2f&EgLtN+2?TL;C_{dV^WOK~w`za8``)d+``4W+sID65 z>T^z??yr1g0~Vq=#@SFEW)+v*!L4S!*OKQXd?q8SW1+DAO9yJ>u#NLgWpLo@Mbe~Nm6v_yeSkR z6Z!jZzN^gxyiW8wo`d^-ntW4XuXDb-az9(6qJlqN?cfsJqVRiES3A1FSyi214@KN%XUQ}oo7tQXwsUzXvqi(9e$W@j*pnV+y1~+U@=h_Fl$+`+s!} zCApF+Y1znS_T2VFQ!Ylcp@9k@?{gLnu2UZ4F*R{IoH?FQO^7zH*F(4@t@$2)K>KbH z14EgO&E$wQjH02PovYdIA=^|Fix4|2@9*{5>7dXm7OBkQe!D&u1SucejbK_L1FyWX zdEM3KeOF!U7S91q$WrSgg4AFUOa~1)s9MKhotz5(2k`h9Kt9z3cP#{xrWUGuq_x<_$;RUi znue;w!Yp)n-Mj^(r*5E-!&m9U1Q0RLV?x#Czfm=5ZvK^Ll~=Jt_xbpy&VK8BCH|jdFkqAkgpfYN_dj z*xpY+-yha&24>m(O0nUTXgQ)b%cmUv(}(vRUByJxrORWucY&QPj1yl2eYIIb8#!Ha zKc*)kaxrvZXj0t(;N`H^=?#5X+A^G?3u z=e(^ZT!AygwAwl&n%DImRabdwXH7+$|G@vgRJ!cu>_Gx6!l@cPx-1rf8L2qu&4I+?bKuOVu#f^gTz+#12{6j0ml0X|}F+0eF9NMxAmpS_+OuJwX1z z6Z)D8?0S8{YW?upXUYYC@0}FFW)ge~`fA?sNu|lz80g~y6{q3kQqSTlEr1!j*!e35 ze!T{t!^+#xFAi3ouFqTRQs@q4TT}s?eTc2; z;An}a{TGuqgqk55xb=|||J05%TpC}d6qKju*6^y5o-19C!o4GkOs3bfds6+}-ZD5{ z8DV)}TKIsDxEUIHkILMt{a)tUr~4>tFh1*s269BlSyC?<#OAi2w7oj8>uYX`fREb2 z?D1PQ{Pd+t&1#knE_dc5o zxQ7|1JIY$Je+`sjDz8Ti;g*XA*kgGEsZ(b$BL(Dfr~-8QvjCs=IK++PX8Vyg-qg?v zC-R^tENpYI0-Ghk$nmD~V$}crZ~r|d|9zMI_s04E;GyvJ5ex;>XfN?Ldp*F1OAgQ$ zL2WSrBQ!One zi z#=9z^V?DCtEYU>2>pE`T|Ml+W#K!P!aeJVR-K6h%)lTkaSRcC@s^q@bl+2N4qAiLNgDZ){Y8?L=1}g+_mYvOC9$(Mb zE#lsFz6z>&e?12oJ1Fo@3q3$kd_a)mbKgh)mw;@fgJ-I%wZ-f6aV7`8 z`nP*Ui|m{v-lNhIIn3ULjZzR@erm|?mVsnf4eblh)9a_;f+Cya>^iA}v!S26I$n$k zw;%qw$`1J@`F;QPJYR3quUMoYHH%bfJa$T!43D7U=kjv zzO}R4EiwS2dXz!=3;twof~ZNdXzz@fsj3pz`)ontyMn>z%v&koUiy72Ea0GpY`sei zqMnj+I;jw|);jH}<}IR<^iv(i8wLuRjP9U%$lj;)R)xsitL-g(2HMRVp9;Q5O3(M0^E$S*snT21`QxHA=LNY~G zXD71)V+FVd^+l~AlX#pdX;m=ZP<1!Z;;vHrFp%r+M`SVkK)VVI2@=`GThun*2S+0} z;6mHK#frb-2D8#*1z>$|H3I~2oEi8mQk(pxHOCK=#)B@8!U`eBu;9yse`46GjlD}m z;`@K zo8)$;KQcZ4!kqf8A~`j38mpP@b>Pu^IIAYLpyDTZ7L_R3b?N&AQTbsn@;d!zAI19s zL1;Z#Hqc%1#&Ag|l<2B_ckRG-dS1=XOHXXi(#FcXox!^`C6wPM_}q~Yq-D*_WpEr= z$QKiOwz$OGef#h$oy&p7Tk2gokQWXR_9tNNVTqm5+p(5^%DIA21O3p087c1jVwq^$ z?~cAOsS7g-d=S}co=ya;fsXrnMZ(CUca-cHgAMb?j|QDSH%FWVJ`AZ-|D;SEJ}oU5 z#&%|&Ru^mFo#6^W4;7@r4$;-LW7r|@^U^6LNLpG}7Am+0CcA_EL#q)haA^xR zZvk5p8YDacy&o~45f?aqAeM<}hNyKQ?XCLV-{f*1tFwr~W-_}9)*qVX&{uc9Jl!vi zs)##4W%x|vlRMtZ)tsK_>T(+X8rbe&hQ@cG1wJJ3W?s~oWIUy4YVpK-MvKs@B7aL4 zV}uRwYk~tEEj`%(abb3@@Wb9uNK@&l1SCgiMrKv*e>2`_MYwy<-clHw4}97$y$p$u z3sKe{!@#c3IYf6khkGf;n>HtZ!M&M3vfA9q4!mpIpRNfPQ2xm`6;e{N0iL&KzvQ53 zT+p>!<44ZlmNHklyIUh{vEJ2ty;x%@WF@Q+wAc-1oa(?yg9okX6-q{*u143MVJ==l zPQETj|BKC^1qB5|h6=+W>HtCjE~$WSdl!okAEzKnpT5R&70U?}+$nTF~L!q=lJIUH?wT#isFFwY9E)ImLmVso!o+d28!{z^J?3J8e(-C(n` zD&KInO{n^s>>)QUjWAvfQ(q>{3Pl%#<@#h1Xc?iI;Yu*)tt>PzS`*QmZE$rN*fyi{ z`E&$++Cm9XSYBD0UR{P_!+pF>uO|o=xKqzj$PwKorR+fbdmExx7 zXYxBVRn(M}<1;a2KQMUPPhfnk-X#?X0{hm0p%n+o!D>6h&EP22TqPMbnSabo%v|+g zXWT359=#kKpxloIz(4npuWv`kpEkZK3eUeN``rO}D9WDROqEVIdnQ?6-l^+J71l2U z<&9p+q3C9vdl=LeiIkKi8|i!(KiR6(I=*wS%I&wiLZH zr)o9&^%AC||L(xilbo?-0-^}X|K5h`J>~Lcxx06NW9t)mXn&=BKMVV zX&;E0$pp;JjqLVA^tcE%RSJsPov)%~0y~eES|7q>QyP{SVq)Y>dl{w{JS56Gd`h5i zwZ>K%6@Bfb?DP_OB?_;eg)f=la;9o93G0-5Z;%kud%ITU&Av+1wh!I-6`!SLJ)fO=VP-R+e-qYG`N}82BfAwuA@305(PKc zRE4-LI?+JY1NA#Gw=geONmMJ^%A*q9 z6?xIT0SJJbJt}<^&kA#GT7?L^3CYTdST1Wu_fPN%@{ppUW1*~?&+?yCiY!dcEcSQy zKDoty)`WLK+SxO;urSLnn^jK4!P(l*qw2$@2=rS+6Cj2dPIE`ku6-~AAqsMv_dqAF za`%l7jEIwjrnqW3o1jQ?QbuB8TA_NjKsv^Ek$2TV>F&ikYgZ`AM#d?ToDL~sdFn)qrrrcFrjCn4ATihW-| zpZ~bh+&OMmL#yYmVh>NSK|#B;xVf}_y?s=alvF>5r2~tHha)1+&#S-Xe#N=m$N~6M zPojU#BGF+d@Uop=XxOIo=GzDk?OQ58Ho6DCMCk|JRcwK zNp3+%NJwaCC_uE4e)a#^;(NTm_oDJ(!zZ7dfV!KkBvladUPF{2`IT+xWN5zT(%BUq z!8jYV&bR01*Ow5;#RWVP^i3)}@n>jgd3kKMuTKwQgNJ8fnbOmoaS{ zFCr?UEits;_y7jyK0wj#qo($!0pw}4(C3OYadTVSh-u}A3B?QW3H4?50U=3Guz>ED z+gi(wT8bDSv`(=p_b==44~*qGcZ)!Oj9B>z!w(-S6Gw*O)PxZOP+9iDP)K6H3XDhf zYFg@+-`LkRjl5ePAC?=Dli-%gmEM`r(YG-5b%=1Td(zObLKy!d=KcSa26&p4qqWz6d0hx##Q(j+4oDD#1;@A9o#*U5OI*6W z5h{5ydWQX9yYt-5YUkC9FC^#rK&0%RH0I;3g}|PC<~*TKfp$BXyjHy?VryIgg+KlY zT}*&4Nd3If@KPE@TKwR}wAePzZXL`FHd!Rg_9c1_KS%MQ+)nS2BoqHnzz*Vpk1u4x zVJ*b}636i`h0j=7p;X}iT^tAJe|V9w|Cd{lJihwlMu&AuFr z3U2==PV+zDmH*x0ePsW4l*<1frZO<-005vj=cU8}gos7=?<96o1Oz9_iRynj#(&97 zXJ!9SEaBgQ{Tpo?%A@-)=I#Ic5>7F|0ZNi=JO9<7*#0FMo%KKC2E@ksA9g_h80P#J z%Ms{WnBYobbN(=m4#fFHCxsg?3};5IC=7%{3XHaMHHROEEQj}i>I>p$+WUnuXE(XM%Y z)Kik+)WQyy9dJ}#SsRYMC0UWsuCrLD7!vKW|NMLx$1q?@IkkVI*l?(mKkP}{VL>vH z@df$!J8c8B;mcyQRMa}Ta^L%P({a9*eV{Ok2P$^4f^!&H2gf9AGy#UF>&C2i=@Ola zfd8wmqPJkM^FB2Ua?YEPuxLTt>VoD7w9E3ilb!p?ABa|i522{|om3kaPo5|_@LvPv z!yLwgZv)Uk!tZQF@q0%`uKrvL#d}Vn>Lqw8$qq8VRfH1(RMr$Obl$(qgB2W2bQ+Hr z$w}pZg&~dqIUo5<>WCZ=9)%xcOdu@qqW5D*|Ly~Vtq*xCf{(uelH*>;)x z&>I^qCKZ;&m{YCGIeSxfS}89PfHjl*q7LhLn(0aRwcMZ+d-HOaOc{otL+*~*kvYWE zrP~6#KzKDLEvkOx_3>+5hNsGN=vl^mnZ_$TlsDhIUZzp`L=FQ7Ygx%`q}tD=lE=Yp z@M#Kb-AwE*Q%lu3#Gq#t-%mzaU?G&E$W@_9ml9*5^pVwzrH^{5l-jkvr_dvJ2nvZ_Z3 zfID$Q8V`P?lNbB3*(R4T3KaeP1=cbJk^2gYPJ&FeOP?H{kr8kZ9GsmUx18*xq2}0& zsEsc*V2P&UWMpl@4X$;fkzqn0YZwHZFyk3~F9$agGJ#}BKtEtf1x|c#Kp+#B0#wSS{3vn+C5n3l z)J#ur<_31^6EKPf(7?zpmq(3}Q!9ii!X+#V_&x{2hfsZ(A^H=@LIzauA{5?Juwor7 zjb8jvs_%te;{NU(Z4uHp9B9({voHAhLx4X8YLMZZKD>a^$Pspy^<^Oz4u0M&%6746 zvEXU|T(XVcFwA$@WhMA`rIVrj#cS6b(EOE^lhgP16m9dAgPKK<7ii4{Hw7$r94Xy; zVpsJrUd!+^bo6imPnd=&5}ZZP7Cv}ys(db^$nPiiP^OqlbwKj0p3#5%{s&?Kd)@6R z(Moz|2cN* zBlYlo2p~>#l@5U-2t{$)W%)Qrh%@uHUc9P(+5Uj0J)nsMZlY>@cdAIMfh@jC3e=p?n9cXmT+`B zGHhDY(PC=~ma#xuEFjfY&1hT5-T3*+s#sOZQEs7BT+id^=n+oO(I^V_`Q0Y5K)?%c zP`n)SHAEFgODX}jPJ=j=V&XouU^l|a)yxyw96p2n9Pkz(ARK!0DOA`(5ZyCHYgcRd z+mY2zL(&jIEhrtDUemz876oPsaQaIaFdmcy5LbkUEyh>kVEsc8F|ihY6zI&krE6e- zBahsToEYX&2psTN^gLtO1ygUgH zbu}fLoX;+2?1Qz0sbdK&L5?u=u|-q13}O8qRs1DMV1!?_s}?2j9e3dMq6&GaA`XZy zq)XUWUmG);n|qdn!zTb5j5IK@o1?Vn)9+xjZtDcjS+H-I zH?Oa7>$bGJdw`2`A6z^aKs`_kDVh&;Cnqc>#g~RHF5aT-TytYzl(d0*Z{%@IqY8pl zrQhLG(^iLW)&pw@BdtvbM;@g0I~b3Jo!urBmK})$lux9CJfk5>4^v+8fuY<3N!-YH zoQK890p3qD#F_9bJ#(>sO{&{#l1tES$DgY8cKCMBtB0GlGW@ojm0)bKxubIzRDrTmj_6gx^cc{t@~ zXI<cY{V;X&wK~8!}&p#w*le8^J+CH?VAShANXZc=a^sL!#htUf#@eIs$>@W^ULdY!B(5gO;n)F;JwP4)-Enty%eWd~ z@qLuN+S%suNTKuaP43i)Dw!zhiQnV_|I@|xP@E9VIl1xsfInY@-UaZ}poK?(F3cywi*B9y3cM{oH@efFmzw)IdKE9#=HKRj z{&J34{`zz^*#$TkHU|BIHw22nO*TP4rGV@}XLFu`siui11wwVCc{t&wQGRkg1P_!A zN$HI)&c~vP^QrHCalFj=d^+>tqT##6wV8|0FMM>7?XQ<3ft2JGjxY!??|=~-15tY2 z^6xibeO?kvzTKM$S$0OKNrTopLX}!zSm3M)bIdRsQ);k`iA3JNsuk+{2+^L283%PF7a~j3iS(>)(=|@9OpbiUaexjy zIiMP0=VL@{kPlIF&}#28&#pbkl3TcdRCRntkzaHnj-c>1u@N-E;Wbn@XM5 z6qtl%dv=JDg4a@Cx8sCfF<=4rQt-pTcpY*h)bgDD%M`*#4&Qx0M{38(A46~5H5MmS zUk|?xbIcD2(HH`A;F6Gkpi{uhZfw*7Ajn-N38bvQQ$YkbN-*fx6tB}y>^Du_f(mJq?@vG73-~Vl zX=Zoja;!ojTVdbmla{-Y-1Vmv#z+Fh;Ps2bCBX!mK{4=L=X!;4>)3l#ORtzrC-kfU ziS7#YZL$|3RzbE9Jisp@6(MK;mWKUx&Sj~EqJ7zWH1D6=5%NC1_N)o$du($k>^XB`+Q)ySIMtayK9rhCS6x9JUH7i@H}sn< ze+ozI8WylBSTd$2=Ni|D(!O=qd{JJ9Ui)0JKYXY7LLu8REMUuleb;rzC>RN@Kc{|a z-a)<(=SqEieZrYfR(u^>J(1HE3SQs2Lz3yjeB6Apn4B;7mR*z@32qkZ3W=#;!sWMr zG#h=*&Pm#NXis_K5C2$9b{}#mq}f%|g`M&GV=)DCTx%fe=eu_CD{=i*0Bj((Hb(gR zE+#v$Yvzu~(joE+JcjO3%Vqkc1^#w%)`6BQWbvd?bCyZypW3v)&Jfl~L~%`V8J=fx zTd?2pHka8^e?n$hZ74_RWJLWJCBHr^APf+?|_JsYr#R z*Cz&?^{$G8Pe}DeI~3bBR-I$M1pZVMxYKWEe%uLMcGR{aj*%vHXx?aV^<`dzyZuGe?9%X6x!Poqg_*oUK9+ zA1Px^@db%d()@zUBNnk-K#)eM6LFk)3Md+-wkHI7Y=udPJzVaaO+2o(Gcmj zxMLCo-$T+B=brGdWLadu?Bpj~pQhDLpLk~fT`=u3$@Pnp{iSse+5P@>*nB&^zt1$0 zW%T&I8=q~qm^UxFKbRePpwWvOMk}D=DMD)k&SuLRRQfu7`$&1Kds?^C7es6C?s#5M zcCND5`B^7VXQC9#I zS+Lh=-K45_dS|lc#%EVibwzrlA6vD%ro`3O-&J*&@L2mxu=2h~sF5EmuJfoD3G<_7 z#ssF;i-pXM*A+YzGtrA+u}RYj7`fA@jV4Fwu4Y?P$yd5z@i}o_R%dQ%oxTkFF0?JU zd|XO&&mmL%Tk1yV93;hWva#NDZ`TEa4k4anm}(#-Yp=qSmO(p%W?Qw9;~?T`{>HctPNN4$OVvI1RnmkTW)$LOr~nT$}x zL0VClJ0wfPJ+0O}{*2VidwI;Opz{MaMZ#n2t>CzYklI$m=ERTMo+`b-m$h>g#8=g; z?8|%m)>Q>2v6jz9bzG!Z)iLNN>9!wr`v=3($QQvWmCdrBSW;*+c@O3ZQ%JsWX4um> z9QI=MK2*yFGn#G}ZBkrWWPq}a``Q?syTaUx)8XEb!?g}iZMF}Z+~?$U($zEzS^ z`HuhBCngmw2SkOBKaVycM#Ex6D@wPnC~KOCUe~m*{NUa#L>NGk@51#hQasmV8yOMb)Ca=Hwz<``&mP1r z4P$-TGh|m?JOI!GJ8X5MAUgmI*xDK5#YyV^wR`(}o@T!1{_E1xn=-(&eQsS$=JWx6 zFyQxYuUk0W`&R9|BhO+3RbRjMZ4Ra`n$=mPyL4Um6e3};({#DSc;I9Kq6=D+H zo)|ux&ILX`$9AAn3Q9LM(musPch}&HWj*Q)P$u49JNKJ|tv$i%ecA+MxQm#a6Oa$M z4bU~9{y4ZI2?!+s5p<|J%h%$!pcx(4JT&84YrgNx^j8H5nNv6 zX_QchB?9ow*P$WlVBH{-m2yA|j6qNo95j}|gai1mDxI49r7VhF$)a9u2texUQl90J zH|*-`fBu9zHGd=wp1~IQIe#4fof}g3&C_B3#?QX-$|+yMBvWwd>?E40&DT&6E`S4A z{U`j&<_{``;!nhn?t%3U6?+46+5CVjg030<@Ipe#vH)20kCOmoM_zN*^n~tsI6g`q zN{Yz66a7rxMa8p=Rd51<@HJphSnk9N4$1c+L0`I z^&>eyj>Lxf7M0ZT16*&zi5Iv>hAyanvDQMH#U&GkcfhMf-0fe=-7Zwj{u{pVxPW(> ziy~=xrmKd)UxjzZG3jCLCMq3-F!xm`fz8I%?Zv3C1Zn^c48||X6Pr=HM#`X z_-03bUS)>~>mWK2c2l^GlPoim(#{no9B*CwwL8KmBcJNg!~5h!#c>Q&rZd<^WER8+ zjNp9BG3{icVBXYI&;y`TV2V9yB8dThGU_UJSg9vCkF{C>aXz{&M^|R_(#RJC5HV@0 z=9X^N6Mvs?Zi1uyMX^JCPJRYkW1B*@#2Y#NoPjhfbQ`D)D~wmXDJa3Xs@H3!I$j!^ zsM^)s%d8_BcjCUjdDMg5jku!OeWRcRsDH02Dmpx^F(FJ9cF9YAdO&Oun7v(hDPaxh z#HhuT4mX<-JqbIC@n@<2Q1mCnvv{ZmUIo3i86m|VhGKu42@Yv9;$1c(Crnkk;gX+& zYLkobbc^%iB4asGf~~#3j-_LEa0OaXWzi2 z@{1ZA!3PSl8ry@H@3tE9Joj-F!nf8ca4;E5!@lg3_=RSlLEaBLZ4Xkd^62iV5xIP(jYu06DDW0?}sGL?WK!WD}GKdRcE zC3S1xF*3liNx|!u5*}08$U|y;-&&e4zAJ@YEkC)B{o~p2Vt|$M>05EgSi)TITq85j z^gEbuz*k_#ed{2-DhmLwN8HdBQ8m}lp2)=YH9?7~mYp!(z3Tj!QdvaKHWH4ypDL&H zStxEZN(3OuWrf&(tKCcHZzkOdRFQp!lywmwe&Dwv`2rKzWlN&8?7x{GHbnLM3e55s z|1QMFo=N8R|IkAE+g+*`0lp$a7$5#+H<;~Tv-&yz6RkuxHtv7#C9-m{{#Q2>Pcrv4 za2MYUxULZh6F6Bbe-rs6N5vxpOUWdS9R|jHLl@D=Moun*8BdOsh$o?xM2==o^5^l- znqX5Y^2EXqy7{uoE0`36qxmWb&tq0;WvlzT;QM?Aay^(s6lo3NR^SX?d}V+O{PiXZ zcs;@ZLibVL@O83l4ZN`2{8EQla{{;U{+Uskm%!g;>mhlon-rcXVGy#pbY+*^)nOs#dK~Ym<1n!$f)_ahBXOB=q^_b5j1me>*&YzPGK0< z$pZl)EKI7I1)Kja5Gs?5^d?7#r7JFxK@>AFfI1synvjr?=ykAH{Cs1O^W#Uh-beyA z`LGe16L;KqVx}Q*!xvbYktHRBy~IWomQxWNwIwCU@_m|PpRAn!xWJ$+yni)B-8qJ@sY@48u2h7rcZYrFtws0GxIYM1Q_DR zn3|bcQ4tfvDb3W4{JFR#`I3mdiIP$_=97rCtZXlAMn^Cev}>1&dS8hs6#oq;=Gc(= z+agwR0aSR;>a(`J^!;2sQ3Rd1m?e@ZDj;Z*)ONzDC91592-UqOh-h+dPU3?*LYcr9 zZ5?e)8n!pcvTFC}hcbHTVBBO&M>tBS5H~hNDcTld$5Ya*EPeg8H9=r_Q&UPxQBinX zdU~1e>CORp-8LNs4fY#lr+9-oh^<6OM1C$4u1jKDbBnhZ@Z^Bi;2())|5rc{F!=u7 zXu(9v*QZ@TV0BeDz>2w!hUwF&Lva4*UDbSkPR_deT9{mR7_WQcn;Uq|98oOXd{qS0 zw=!mm@uFA+m>Xxu-oJ;USTaSdOw6pzM=z5#E^TC-P$@5eugnapONNl@3l4P`At@t} zfsoPBQBgs0m>j)*eZ8V$VzGz}!MGdzd)vDrNo+bqvd${Cq;{>REBW7|e{f***hTvX zr!Nf?XY7a{9Fr9*B7{S43vo#asiFSCL1f~Z8rJD)r39?a+}s`+iJUHGrO`}c(*TZb zawC2Nc3CI3#8tpF(DMjNX2$3RgkEyre8SjNW2y@=JSEZ*eu1O$=fgjm zL;81;R9&^bB+bp$-7xSi;F0`_!R6ynC1MWOe-m{!^@w1bQb3JN;}HEs${nEPBqe#N zic3{TBzn!eSA~QT6&BU5W_Np*RBsHEa`y`1vs_ITABw5o!Jn2E}23_k_T ztPLH-%TXdcp`AH3PS-T3`1G+I30EsrvELiBBM3fbesy)u_2+UajOT=y9!@%GG1 zzim;8;hw9@OMfrtMZu0{RFfO8P>lIJNHABzZh$~NzivzoAFS3~L`HrJ zG%+^z0iPZWPZ_>eoK7_E4Kf5UGI4wFCDDf=M}Dnm;}WTJJ`a$Wq?gL4Po+0S3yG$k zt-X>REel+jS%ZpSbN@w6LqXH*&Sz`BBHL>nsekpQFKQAtdpff!A#a4C+>RZKqRf!^ zK+MGQ+je`FYS7@Vj^yIo}oh}JA^HGBZY zM###KNfypqzS!EIwQ_RvLjmpVkA|ny+xPePv=K!>_?S=%GNCkY7AS{bkeA=1tgPc+ zK@Po^laGn#10_LB0BTCPC9KO23|l;QMBQXrH3_=UH=pglXu0b7s_1B(ZJe)09zKHh zNQFFGtIxqa#!A&W(^*W6{JxhSr#;Vyk!^j!9ePkElIxt=K#PT8MeYu-v^qBfiZUWq znp!9E$$PbAqngO0a-YPSnuaM8EK-}p+P&9CtVn^2c(MNNv1nMMDt?>)|(&xnD-0T6B*byz=OJ*Tdil*A0 z4QXz!2X@}v+@t^z!*u^jacff#A6{T3QRCE7gGSzlzE8y4!6@uwPy3-$mkPUEEc0ue zxx$zY0*aAarS0Fvx9RqVQaH9^pj!~ie!tv(Sc)dBCD`d^cu)vQowmR7`z zYOtvm=^2L>e=QpjlBVZRe4yj=Z`t&+fU3=yy^!H|e(Hkl*F992{#f z|4~oLX6E-}foe4^EPHlrUf$OR*}5v*HqWdt{~|0jF@`gF-rDIQK#wsjGv)E&d6Uxj zWyv0bzM7eq>3!*Q2#*QnrQP}|Y(Y`^0X{X?Pa`Yf->bQu>2{8pm&VX(In?}-H@g^_ z&`ONR!?^Du{xovEk~q3l_KQRS)_w5P-~gT|+-5k4Ob~^b;PUy=9j>KTS~PZ@@Ziw0 zQ(H$XG2M?%aLWUbia}UrFIf+eik{x(9{r+&hwTddF1EYi1Y5%E>0tkIe;MUkjMUq8 zOD&8{^T&WuC_B52E7G)MTG>ED4P}-G3onR>v4x74#Kqab9(XWu#3DzMQIQABoqYe; zawLoXr%kg|&`XA`6Jbj#>-?Kx59kgnzUtF(QJiM2o1R0ns_*ITxJo%Hxk-V&JQtNV zVDR8;N$?`Qmb?iA!BC+^LTRz2DLp29@bbs0b$@^H#yrY|^14|QG2b5o!!8~sH!iz` zT`LFG>KM`LY*HG`2G+xKTJUE09Dks*3HnLAkN;i$HP8Af`l74ttqujxpoV|N2d43# zzH$jGxsJXv)S<_bv4G z(bV}WKcpJ~{j4 z>o8c4+(utexjTmnN@-MX;m7&=vkdtmcdJ~!I!p3T7Q*`2`-9FP^nf0Pv29mQai9UVs0WKuU_V_Fk2u#)RC~Y5WKnry-0B8oxk^KiCQdzL zAj66Ke~)9Y!Ai%7qE8HePms7D@yq#6qe9}j4bQiL>S&$pX2ado%ipzVee~WPczeV( zPv%Fqp+j&Bht1R-*Lg+G>i1$_gxRqSVU?DIab5Yw1q`vkZKu5GZJ6Rcg8$haf4%l( ze?WJ7J2LR92k`BqJUZhOf6c`3Re1+FtGM21W^m6HixNb$>^~zVIS>yf$3LWBKa;OboebAO0f({7G7FQ2;tlJnVaLdZ?Bsudo_< zGmUfN756yof-!9zv8C696a7&AQYu*fGs<-CLq5aIO}tj^S06na7PdcF_YXVcuHqdN z{4`|?x2QR$bHTo3+!O=t{)jkJ&gTAm_6jsN=Urt{mYP+04@r|=>Z4rVPx~}N*YJaKr7FX> zy)SBHYs`jz6f8-|r z=kxblTNg7Ya%M?eBNsDqGZP0>GZaBV6lWJFGb1|`kELJgdJec;xW3s2n@vHb;DtZg zDiYGv6x4aR(wK6Q9t3XTremj{>-kD%ULLYc!fmn1w<_kqYS;uJlWqY_KBSxg!mWEw2yWF0< z+~2*Txgt~1Y6{cCEh<1*eh+B4OPA8Of!6#RWj|@?H< zWdNr~IFdG~fIJXMiYX8&4vnQXr-|-P^bQRr zwn=t`B_l*yoTHvbVlNph2#JnTQ6hr3f#W-_orohft;Ac(A1GE4hHuK>1r?ZZV!h#M zN<%6dp$eig;y~86i8n?~#9-q_jmKag!1?5PmfB%>lKD8|!1(J9%Omh=@_r zqqO}q*@KduLzx$u zQDe!lh=K3r(RTJ~eL2a8>6hv(SK<%T^`=yJUYwuf+LSVln>u{ex=t!3UAMcJyO?>? zzRat!RFSp$>DIZ5Zwr?qtWrEX&-LBvJloiHOH|Oq9G=9D6-QD(SqmC=x&0sP zy=72bZM@}+1$U>h5Zv7%xO;#=aHny15ANA$ znwom)DvDhVUA>`u^E_*<|8ISu{5gbkS>hukd_WsTr)YPV6B}90k@d-gHwUfaw;LOD z@f1_ZS~*AF__D6cnP$*7ov~QsoGx8mSDjKzQAgKfH-0-kBH}l15aZkN;9~>hx0RFmnYot&eEMR|+sa(j zdJn(i(xkUZ<0WoV&F


*WBN zn3}8&TQTwxa2<0K&(d)cRHVb6R(@b^MdRaPTuE2ChU+)N#P4 zMo^sxKl^@P9<>^Yczi?Z(iyq>hbuWd+dq%-{GERJ&(690E8PAo-2N-v{wv)6E8PAo z-2N-v{wv)6E8PAo-2N-v{wv)6E8PAo-2N-v{%+1(<>Tdj%d7?p$rH$A& zllfiCjVI1Cy1AckweQB|(t|YeYJ~LzNYpp$n{W@cW39J-t>YS@zc4fV z#t9u3f^rSr3HH=`nNgp8?07=Jb*03p-fjGVu-74CR2a8o(iuXGu7^yPqs*>qFy;CH z?X8b|Khb+S`*DVqrdMNSS0An4`A>n1d%!01?!5nG-m;P@>7gvV2+L+;jlKuS(EII0 z#slB3uX7V}cT@z+PN)mIhNO0nxB*3AbgK{-NSr^ovMG{}*6-$@LTZ7Y7Yi@I^C09$ zND)?gP}0=z&Ww-d&8+vi6>FBRj?^6pJj}EZ6WhF32~&lX*FJ>5?23E~ksp+U`ZT*> z6WY|TziCKE)$;4tFEWc?zczaEu_qUgjWNN+utb-7qUNR9jM)A?zu$+4hiL$MqGXXa zB}5d8Ml$fhFI!{;B?QIEPfpJbO`jsovW$l58;p%NmIen0d(KMYa1cC`7pXo@hB)OB zQJcY`Zml-VeN54`HVl_qdVP2ZYP!5UH_p50$E`5RE(_-or&@e)=x>J+TT={#vF>%@ zhZRX2Jj&#};H>Z38vqs~DvjdJcsI1O)|ug)Cgm zQk1F-@Xc|k;3ME~7ez|(Y{#?o!nh;Op0S5fx;DWF2WdX#hKmzCWc)0+G`C;(4&nQ=wjN+_^gd+D&*FdBfPHw^v!C@tqzt$>g zaCsOIFsJPMGhrrU;+K^(BjcLY(divQxM{{S@1hJ%EiL^`E%@5@i$*rI)DIsPoD_Nb zS|2}Pb%(j9LPR!9d=!aS+33fWt$bw$#{nuU>#8alm_PjNWmJ$q_fPe{!*h2|4q$2|7=9&@o zIw%;}$k?!Qu8{j0A0#!jO&*{)aX&dRs_aT*_d^lIQYVb4(Pb-^d>JvxT2wp@OG+f5 zu@hA#$V|7iqfken=0ri4c^-}mdWMlNpeI6aY`E__`WQBDXWE-oax z5;{-jm|aH7lqm{~U?0w)WnB-r+yO?+JIP=$eB|4OjEz+zZ))?Oqmtpl5%h22+;xu= zqP3cI^~tgWeP*94&@F9nv0fNvZY7PPekwDhM}1;*T#9x^tgo-b&eSM#p2l`WD?fVh8oT7=;5qiZEd6e!$y5{Z=0$g7Dk}1t7*{aJ8AWoHh4_v7M-$| zAXx(Fi36_R_QU1U(NJuxOt*ocZ43X7@87a9go_i&95MSdKR{?ytTcLdt^;RJ8kb#h z(9ts&k0@Fi4U>!Ku!ufk#`OmAVX}Y{^7V8>gv@HOUqLlaa8+bxk^}gIBU)-r!{FJ2 ze(gD}&f076Aobds?#sHxETqU03_tlm5T%5!Qh0OuN>>!I$!wa+usOTT2^yS@QaB@$ zf{9`Acs|A~558F37E+iF1*^Z?{oOs<;@$nds~>#eIL}X~FY)fa^8g!)6+REw{K zXNbC_#N4{1gnCFS54YArjX?$*D+Ruq4WbbWDvC}@hDqR zp|;6atBaR|tbmAsJ?i1Tsxv-pegQsSL4G?fW>Ch&{^Qb7cvz>L)k0HqW6p4aDxP7K z9_$wmV?Vc&E=EuT9PX!ud?jE?`&Z|?N;0;JbUYQ-%{@^It9HovCBN(Kt1ED|3_vI~ zb%Lst?5=m6M58DPmX@xeGd;KZ%Wg0)pJdqFqCafq%dDS?=ge%ojfzWx(IMqgCqbv+hb^l<;9s7It+!qZT3^>&UuGv#bwa`7!Es?x7!~N}EaA8Q}bly3M|J=jJu9jVN z1w{60rVOY~)ryozkJ<(dfH|%zpB6_0?PTDW%x@e8PPISg-02eOlbS7~bpw&N+uNU? z^}GL^2fSWW2eZ$jXMTtaj{70OvEKlOu-p&FVJo8M-MbMsRymS|vN4KA7CLY;iZ+`8 ztx#ABx$1%AlNIBcbl3T%@+J`Pk#|U|d0ZfK>Wkayd@jt~tg_*S48t#J4q}HpVw!DQ zD)~o;=`2Cv9K_j*SfVZ-$Tn{1hkI1 zixBLZ6U$f++KV);7OsUg!G*YE27PJ}&5isGdV)crbGjY*J{NxZqep3yXz$Zx2&9-* zqZc!K#bB4=*JhjU20n%UEXO>>r$dm62VC<7Xs8kB^nQnqf4Bwnn-P z1_P%a)A6PRHMzwP*UP5dXN{R2&M1GAP6aYd-X-oL#;Ybzpc14cya!M{9hlhle5JbJEsxG8| zSmkPm^Q&1fh zLxh1!mO4sK*OG4M^enRso_9yZfc{g`qtRDF=g34p1N5cs&AU$Lh0p1!$luLJbUR2# zs2kbC)&}?P99mzMSA+7spj#y_f8F^Anxiuex}Mb}K0n&6z6v*_YUh35#GZkE#%INE z#~t`6SgW7JZcx@;@O?N%cI}{?@Ca!1xhUn^{-n)1_de1|5rub8mku;zNt)}a9~WVG zXQz}Mud3>(5NEPhW1)K`a&dUStZ;azS>ycDWdSc`*MpGDYs~#f0d|(t78QR6g!7iK zFY?z94U4<;up5qvVGsDMlDK#it???d4KL(dfD2R%ZMiwyQz-Veo#389+%B}MQk<1R zuU=R0t|HVIi_%6G`0^LWP-xw@LcP4$g$e18CG77!3DkZ9GaHHcZky?wlO&FUUVa^m z`FPuJo~(Dw9flvZ4$E$baX(j*{{C-k%Kv$hxk--wAT;buhxLzk+dm`Mc>fpH-)uZ! z(As}zuCa20^A-Qabh7MwjieFG5bku5B#H#5NnW@S-UA}aLKqQ8Omqqb~la9mFq~H7~dBCHC7u!yA?XF?AO|L0!Aoi@;Gen18x;CHKTi-6gUu#V>3; zz0zI1FX6oh4Ni?3<+@LFp-FTZ2EU>CaBhuZMe7eM(k8OTj<{^K=h;c;pX==V@fGyN z*^`uvOwvTg*Y{0brs`9;ztIsP;1-DLXSrwXx&9fRjGT#i!6N$XB>54BkHPg#bQ4|& zhJk3b)#+eLf3vt4n~KNl?WNTz2;Thy3wP6-nNUxsC-{4?=pGTPimIwc_prPeZPasl zb#*c9K`)&BBn0YJ1k^$MA`43tlHrzGSoq)%xm3dp7RG5p51DnnuA+R%jk$o(-mQRN zOT!HH`fr8kEMa&7k$_U5KY}~Z%If6s5RClN!|3dl(IqD4sjT`Snkp>i??lAC(IS88 zA8#*$3W-z-5rOJ{c%<7|&dds3o6#@wTH)#WfN>0ke&LV0`OzMqP`1oY)Cm*$N;DJ!icO7o&mXm3_FBCH5Z#pOU<+sc7lQZurILvL8rn0 zgBr4MfB)?4`0DES^dF_s&p=z-klbv92mC{QN*?&E$YVSvqR{AVAaE#>5&>s_^#cs@ zfus5cXn1iJTbiM&vdYHV+U6{&#~MuEtgNc4VqokU>R%id);~wAoo)$B;`2)RN^6o- z095=g(xcpQNTwB~Fu}l3RR!6<-a9=#JuO=<;53A3}*!=tuo~YW^_Rk*@lALXBG;sQXn_B}9k6H?j zUmJF45#6D%YM^hQC=x*TOP^^jJ@7l!>#L7Xhw#wANH40jeALaYGi~9la%in6Mh3Ik?zM*d zu=3>xd`6Rm`0!TBmX;O|_W)S~d+Ec*i;J*f8qJ2`Xz!?~DEMw%TmTU^crfHl#@f1y zQ9c^E+L7a6X}by@K$Z$2sQ_VYX&CM9aD8!ii-w4R5*ro~6CDkP`bx{of2WLG*wi&L z5)~Eo*0GjXGkItPZ3_F5D!JiFc@fI78;gk+I$+Ram;Wp<(PYYhu>8`Gj!&(DX4mb% z3Az9i&>n({c4r$JZldIEzZr4<(UX+)1XFw+jYM_i)%qJjAAdSHIW#n~ac==j?ItyVs|vF^Ucg<2eV?Ya4Gl$nef4Y=UClhT z9F4rK)p%+z?Oht`aY4#uy=I!eAZ4~}J_QNM9kvMOo<+Vk_lF0#V2S>I>L11C`@1;U zIH^g=so;qD*YFKGdb-guS-A+rYo~hlnFOpbg}j=QOssrX&@V(6mFO!32~9;5GbF5e zN^Am~wF+!j7^;sH0(4BYob3EHKU*3)>dM=So2t9Iy7Ke$RmNf8!8L`JR+iNo>PgAi zdj~9B@!;pGCv0qL8#SP<8hDVnnkNE&a=~%=p9;-^Muuk)EZ}!oEdnjU(xU|Bp88zl zEs6rSEf;rlLsL`us+P94Rz{{B7w7!K9E75S2-9M}gf7yZIS@RcQUjFxE;-pZA-d0j zmQD|zR+x94grQ>NqGhD7LD#W7&a~xdZfbN}{d0PnpxOK&zpbTla26AzZ3Av3{0s3m z9e4yoyb-3igm;dCR)!IDl`co0B7Uwq8{OU`2S?%VCH%-v%v6Htp(p{D^szHd*qk-D zw|RLzKfxIU7I0nJgQpmpODgJjzK%^u{-k9bosj>K@li+#4-VXj4h<6xONTONkRBY8 zAcM&UnWA-y!P-#`7}5){=HpN``=!c9X|mZYkwr9LCA;|Uk? zCn_d7R%tbpA``Qo742LhYJ-@VfaLJx$lA`LyoQN+a!fuv77<1G^O9zoG6-}&0-O{q{%+F#{fB;O202 zGjjB_1<&N+ zEG~~hcmjw_Y@}e0fA)hyApBJ}JV)QenHiBXd@{@nMdND4`c!T%mq9=&g zpnnHt=BkJ=9kf zGbdw)Pz>dLmj#fcVz<5$coi#5WQ&u#ooH@*3O!ex9A{qBUs+uBym1G=Y7kD#(xQoi za$}oGY|%4Y%PyyT*H@|(IQJxY%N({ebF!?10Sq-Df8=@VN_m0wRqvlHKTPnF7dXH; zew0{+Nd|_R&)~;;**yZYvGjXf#cg)MQLd>R!6n~@#@azp6!MKT$AU)kt~rHd@RL^o zEP3`%K9&6V#=>5UbqlTfS~e9nkB1O(s-gpxVdopv==IERn%`C+Qq8B_TwU+)^ymW4 zz#o4)-0z&dtejpyOH5?HRdc~87=CpeZxpsG!um=rAW$|!8TdnnhW+cEKsp-|jY%7< zB)APC71E4Q2g20vz46Q9+gn8{vzuFvpvTQc@9V(QNbQy$E=Q|{1BS@w2EMJ<1RTJ( z$Qc#L!3-O-=n46c3A80?!N?KgtH0?2mX>@F=-}Ys?2>y?QP(?sUyt`DuS;S{t7{nK zm7uTni+XszClCP-yd`45s`ES zzS3EeqiqnAbP2lGzdPPP&PWkjjX5Q{(E^X@BI*(f%0P>9h$xJTU${B2*&8_WAu=@= z`Me&2#VJj8Qy12)&%y0JS=xFl-Y=&E>u)E`p+UQX%^Ure-~BFbF3vI+!ZN6}hpAx{ z{LHPy)EyLMD~musF{5;U60sf3{p3)A#2uAa`EYEvk$rg-{7k5=q<7i{m8QS(ekWpa z+I-*L<>dvzF-2nc1r&wyDZ5J#vHJlYM154P6bi4>jn7$3s$A1ApWJyl{BSJAV`ZRwR*{J~+hHw8UW^sBSOZxev zr6$O_l4(+Y^{72sR5gqkMV95xb8zv4)dkALR}(ARj#)EnAC;=(`-l z6#3YkFT)+sX&WDy{35`LCGYN;Rz)i%ZS0(igI?N;2eU ztZkq;NEFGXoly$mcJqCF`xrI>K^?XghGkpQ@prq>oA7LhkoXUe+Rnx`jUvv1B4$ zeVokS;c+%YSEla6#^LDLhRzxJ7*_L*;lju5DnSNvftcQIr3YVCT@V;oK8g`$&@Wq8 zP^TiV94_%)p2>?y_{bgf(43j!r$f`RQxAtFA+=H({u+LfrNlS+Sx7*d5{EF9h(+y` z&ea{HIc&gveRW%6B0j!{A1%B;L4OM5NngJsQdtO1rbrRq$3-|?0#>wb2#y@fq##O zH+0OCs7!z!1trh|*7eyW8&lHSjX#LpewQq}+xE=_gn!sL@@dV@7lZfAOL`*5ToXc1 zk421+_eCH7+&(~^^v=iT=lybVvX!aP&U2oG%g!^Kc)1lI9D+I=8yipoSZRYDD&E&1 zv91lI&pLdKp|+V04p9p&g<)rNc5ZAPM7`y+oliL^9=HxP@Wz$O{WAJ=;Xj;J5{nvx zI-FP^Aq*RuO+5^{jADy_j1}JZPyCiK;9xH0WaFWyCGgDf=OIkTm8)#n+cRV6y*PrP z%qo#CvkRBST>*!^JWWO2ped2S6n*S{7^XIdoMEWP4%v@Kxna8LgBWeD+Si9gC6| zCxi5oY#};(QKnT)mFk(v?w6$i`vY0Cap_?iG{DoT@U)V85z*Ak1vBNfo8LCGO*Hn( ziJJfWWSUBj5}KMxQU9@!gPb_8<%N)_BmoFw{Jmn5XwCC=6h-o~kc>s2Qmk38`x%ye zE$OqviOVB>v)({VNWfzja^t16Z6~5&@rM0Lvh9KsTZmD`A0 zVivYajSp+f9U8$dCf10=1l`%E{5{Wd-b^HbcE|BOLCHkFbJ zdC|kMT#!^{YjZE{rX@ppRR9`G;Z1GRxmlj=cuD%|20D z;9);qioX&z_UDI(>Pl*BXLN=(?e0HC^vTnRJ$Uemv5UpqiriS`mY}MFJAl13Y9Qtf zJ&6~lV*pukiq7Ssin3{?9`*;DlmK~JlNdx6;v>$5WW2n4uKv_nagbp-ZS$|Bc*E}m zUn}+bL#hk&Z!7$Q5GP=@Za?11!As#j!BU9P!yV($jN5sV6PLg-9| zOTqz?Qob5&@rK!_Eq#ZlM?-)tko1U_{6QIGI^9ICcEdw=$-M8hTYX~UdD+POZAB;E z;sp5my(4S6%=LAJSzMeuv5{jua2Xbsj z^BK9OKfI#mc&^!CA&VEgBpk^m5|$6@iDT#o_fi<#R}pgVeNgkKR2ZrJrkr8~+a5z@ z4gATI50m&Jq9Q>|!469{Uf)vpc5$hikV|%(&OM&j^y$man+OFQB1bzjL`ac%@nGmP zpEhAl>b}Dzbb`|tVZVJiDu7hgDSF7J+mB26bp5Yui-@e$@E~Dx)9dL(bLKqFo2xzg zIv)%|C-gR1FMfUsEf|*T8LUER!87g?Ix^>AHnC9w3dljuND~vRVf$?QutAmkD*ltJ zDcWjrkh53=(9-tk?P-6WYd0&cZ?5_Qg-?e$2V7wqir%J`)(SULuu>I0XZpc9H`LZf z1~R2^KL`yC=um(u9~{ELZj``|gwDZ8+!M=ut4$6tPzKV=4)nGpR>iX5XyH(2$ziC7;VgF8_+)B%glIrz|NF)5M>GjehU z^jJz%Nd#0*l|c82*H}Yf?(A|8Jn?J}Ua z8->)(zmS0Gn7Mu&{s<=8;4{T9e=`w?Xnkd%ILBbQiOB(!qJ@Wq`rPd?K(neeTA;GJ zy1blW=rm5$&>dc3_~Oz&pf8Vhit?)|usWiliTN!6>IYT@2S=w2-nAG$Kq=RM(<+|z zGMv^T{TqJ2YFIuy<&PhxrfCA?M)dUbjEuCjOmuX^QxhZdN_)`e-(Nb`Fj!NNqf9Cz zR!#U20iUC!?&-vK4!8eE7)SIK8tniTG0eD4O%HZ=cl)lyt#*mC#z!Vc$ED>4U`iGb zr49WP>pRg|Gw{QxqPF7`<)e_10P^9#vOa|jj^biv0UOkn4;#Zu@j){Er8P;jYRD~^ ztv`y3izuKG2Yn5XH8kN-Ui%E_`7=TXiw|X}1`OHB`O}3%7+0II_A^Rq=;rf^?QsmvcAyE`S zU<(eRWLbr|Kdqv7Ltc9c_XG$D5fufcJ%rWM1fziS52?Zw(tx+f_|)~~)#k4cjN|eT zs}jf_f&eUPW)pW1C{HmY1oIe3^rH}~SjF>u3^SDqER4}j!2=@;%f}wl+VAO+J=>>1 zB{4jUx=oxY_dNsboHprpLF@Y%kio^aI#=K9=<@vf^aug8JZt65%-9&=HJ^mt`nn!| z!*TC{GvDG8d>|idH0D66L==Ri@9@u1YOPik%S8wad}J^I3d_KzHX~OtR@n`sfhGnOM~Ov9gNYhjGz=Fc5wdJkKJCce z9V#jZzUC?{>Dd;;gj`eIM}|IOgU<}<6Y<56#fVAzhV4W5UO^$pKUwrLl@^M(mT_@Y zQoiC-R$-e3BmBngL$(NtGzsHiJA%Ovb`0MH{hlZYF-_`$bvnL%5O$R}<_=D0Wyh;< zxF1%*0*u=Zm(*EZ_wI09;2yYJ?^eB3A*12n(O2||N*X@3EK8i)a5OeR!X++Q7<2{% zkY#>Tayl8X4#8K8mAS>H=ZGQJ3^KeO!dmJ^K#m2-EPnjKR9dF`1%udzcpG|#yq(U> zm?WX14Msu^$@rL)>35!Ijx;>}S7U5NN)o;#WuaGwBJ8R!FmTTBvBfF4@Wh!h52uT~ zAu#!Ur8;UF?ZO^GlrqCbc2W4>e}Ukw6>~)xVTnMHIgtPe7|~Hfdv)me@0u#m$=3&O zbg5H|S$saQ_QIhTI?AfhWP@SrDyxz)n3(v5pMsFID=?Th(qsg3X{>2}0Mq;az&;a7 z!g)^ypquxDzpp#4;sRf2q27El%j{eK$=JP*!ubR6cWV|DGSnmI526A!hZWmniPQYF z!47U8D3C|*zv3apO!-j@P(xr)?6JL}c)!`aGQUr5h_w2?>ifQz*ZlaD_m8(8gcm#( zx8k6GGK<*%vC8H@3U2T`q)dq2iJM>UH+%` zgx6IbG2CBV_|r#>ICyCo3Mfp-|9<$O2;;jLLmDe>ZFlH}ZoKh=q%1@n@L(in9rsMM&_u9|(=D=m(j2$~I|y_rjZw|wff9M%^m`V1ucW%KTAl5O$M2UEJj1qL4!au>F*7pSoT=&@h6z6SfXy2V48gcSCU;x? z$Y}my|0oJcsF_bXe0*(fCEpIi1zT*u&d#BMzA&6Ebq`9otm8ACbrgY^WIvBVklgur zdOf^PP(WHBmoOo(m!uNf%I^=ngVBkEp=WWF0jUIPgXA-iEP|U*bYfy+QBz zaPxN7CPAI}0El%0|DD#e(zCeWq{MUpK>}t!1ae1||0vkQ8P_4k{e*nGJTW1!a2d%B z%O3~LV;{JR&ow$Nrm`}D99|{|nBnH2(91J5vKODSu(63JJg2r5PPD%CiG&~dOn2ENqdHV|c-u$i0V;b}O8Mn*=~%TqZyIz#Xg zTG1mpsbcjNy8(DTuPEkJiVj7}&dgxb<45K5S*^7B;iS@$D_2+d99u&}OG-*oHKDEU zZ!UVcR@ZcTCr5fozSjq>Mj@v!9-8nYbIK|znkQwLPXYkx5a}owPOdJAV@mEiV-j=M)MIAU5V;JAl@t`Bw*w>V|Hooh z9t(^x(iWw@Uy*BZ{Eb>+B1G_8b- zGODszc*f6Jo_rM)2zKbA;^gQ~@;Hyjhev2lO>q-7d24GOZ>Ucy9U^0~v9XcS;V}{L zXebDX%II8p>489P94%F^y8YSucUMvjiMmRee# zp0J-(lLCUlzesQ=Y%v8jW&hBixI|De44rr9uI*%u>wJp1!a0W}O+>Fqe(kO}3Wz!z zgZ;nxLSdzg5B*?Q=&z%bwc6p$G#Sp)l6EiU;%LD@eb9t@oso4PtMvK~@c zT%oAk+q47smDeG!79c#HwI=9;Es~Eh?GQc!UIFoXJboB0*xpX~{UtfAu%V%-rKME) zTU>5tVtP(S7P{QxOUGU{TMmHOs)YkR1z>u>Jai;H!w>$KV8-Og+}#s*aoAEc8+E$s zZK@JpUJ3Sdz=lro*I5-+U0wBWD%Et1Obo+QBl6_dh9ZchXa#155dcrEP)zJb;XLf0 zzr#nvp(j9#QO=U%qU6R{U)budks@Tr=*lfEPmT!znHd=wnVH(+08LNudQr^h^>O)l z)KR@Q!7wrw9Cw1_r1r9MCVBGI`Hm=yvh+B{j1C|M7N>BbvbnJ!MMi+hMWggIUz0WF z<~cbyoAp{2=UvOdF`b>E{e+A9O|DaC`H_Vo0$FilB8X<_J+6O*H>{mP7-0o8#B zXFSv#SWZ@6=@rzhkxG-pyc%kVCbtSzc+1QDeC^fFZug~y8fy7^E=u>w)zxFfsmofK zgeR7CV-d+7-z>sw^Y$6t9DYi$vhX+o8iiSez7g<K9ARvFJA7A~x%@$Hw zRd_9{WaszZvx#n`St$}p3LrW>KRNo-=6z&@^i#bQ@w#4wQ9yHRkEy=7$$$37zFdWN z@m{&`R1In3d%}!11||WV*=WMmaa-|xQ%rSES3v&O^mJ{lLw^u3@T-uki?a#vXLoaZ z{i~6jm`!qdSm{6z+>>V;-;&nl7ctsJAtApWoZ0Zi5@#|Fa0fRvg}JgjgQMy10iV9QXlN02&5!}GXzs8~J z0`*YM-)?0W9T-j^BRd!zH9R#oH-h?QMySwBmPQCN+O#C63qz5Esv)c;snTg+&%hPD zay2kHUA`?#f6Q#g#kJee!}M6y)6s0<6au5Dct7*aZcM1C?iW)_hQdC?&1ynQ1V+BB z9w_g`aF|d(X<5Nf5Q@r;4%5?BRKwMS?eEKxvZcr1fgk*1^z;=KZUaPrp-=Q%uQWv&fZ>KriWg=tlTtGQ*mu4MXky6T=Q$2I%t^5X#phw#L{Hm zHdNi&DIa>sxqw71J_i0X&;p%k0VexZT$X}Osu!$Ojl#sbPDg#6tKDnwujuG#sj#6> zC|dkY7xu4j0K?4O93s*`-M`nh^nZLYE9o!r_iVZNA$+I|&A~xbnyusd1XJK_hErO8 ztj*sBCrMpwX1Au-)`_6PhB3@$9sQGp>=)T zHclrW>#(A)-?CCu&A+Vc`6WOYFu0s6-YHWxNu5F7m(ybKF0xlxCSV>;4PR z_oV3E5uCtF()Dh?=G(j5F+3$wAx-U8K_QpuqIu0KzSMZ>(2UF{<-)60^Fm44!tHQA zgPqOoBRrz?xD*YYIs4jrI};gso!d z^8cquE&OT^#r4vm0-vrt~_X>h*=* z?v7~s?MVGzFKm%!a|Al1zGUJ{`p&Ck?zPU&N^e|&y9&fHt@Q?yWMA(Mk!gQxVkp-QFpKf zFd=$>qhy(fYruLI6|?h_#an>>+SEdijgA}0CoH9P&Qj-O;o`Hiv|qt^h&=qpgLwH2 zuJXy_q-t#33tl?WfUDkArk5xQk@dfu2Q0vN>J&CDxL~(`%!>)6&jH$~IwH~^Gdtf! z*PVJ_qy^nUj{6?OztS#mazj+b&*}**wRG3<2yuR%^(iZpOwJWKV@y`MA@VM4LgT1b zV6U%}8tRRpo(rh<71q|&6LZ}b7jyic4zxJadm!}_wj$lwQS)l5?P?PTdGcjgsUNUm z6&_{uOwU6_0~Xd0McCPz>)cXi<~+4*9n_6ik^?Fh%pHns1UxRE7_)(X0e7UV1GYfF z@5?2zNx7%W?Yx4lt1Hu_w(eG+J+2+xF!amLEB|JK+6*n zqC!4#W&&=DNz#8cHU|0rkc9vq&QeLufs7n(M@0DLQeof% zLe^76r2$84y1OnzCDeZHQm`L@@6YDEFmzeak@ zoN6fi$U&FqOc1<6RN1=s)X=FF)A`43rhB)ik(#N{ny6*}uKSszja_`Cbv;MpO_q(A3rS2}j=i?8y?&m)^R@>GNc4$ZdKGk*C7$P=uQd1ZG zHxobFPfkfF{;a&kcoDtx&3R9JFU4d5I^$ifBeuJ&xYXvCvrs2L$3c+d&6uA)`w&Th z8jgOIo=vH zP%`wi3J`I{&6L25Quq!k5iE$JBO8PaDmthd84wJFg{SuS;_!$bD@+bZ;f(c^Jd1)) z7a1`BC;Z4;45-U{`~Y6Cw!nAXyqo$f>Q!2-=RT1aBD2E*t$-(2grvN^(iU<2koPR; zFo5mO8k-wQUXW2_GCGY11a10mth9w7)YfvY>MlgI>7nEcH6HJQc%Dg`um{I+R>8s9 zKrRwZk|21PIPe?|h2>fI02Fy{KX3$=Z+`NMKJM8lYaqW&tAshbpnIpL>Iv(@ejx{m zE24~dEu=RImMQ&u;ZRW}Hv{BP7j@O-{X`?# zR9+I><@3IGqoQK86K_*;aCbAkMy@?KBZbCQmn>rZE5H!94+YN5$@cux$<}X0untG) z{bJxP_cg-CgouNS=;6_r>+)q8UWh1dqLnfgXS@}M1XbRajHRnb+!P>|of618%c9vU zz87qoQTie~BW|iSAT+k+FXV&A^he0TaxxVw*^5uha%{7%PA~)jN*fvVUC0?XGWFWz zjbf%dd0bj^1G;kCmev}IwM)^xFMR30f-)nTQLebxuHnwLtxP0?G;{kkAd;LrQpudA zKMX#qkUIIr8S$;s1kb49ZLcfd%6bu0c)+Z0=aaX!cFcPD^3$dEP2zfzRXH7{QR`b) zJLE}ZQ}OX{@3fB}=moZjT;gaiewer)M_%(Oi@B#MXXYaVm|^bw0=ab&Fvn zF;GQF=uZrZVAGBYie70NEXPda5r>Um!+n9j`S1#l&tXQm*MBOS0Ib9baYKul&ToPh z`#!OtEv>wdD>{#1go>Zj^RO*Q<<{-q1}T)AIaBH0HyKK5xrc1bO>TIA^nFm^D~s@i zD#rM}vN5=egrY@e9?!YlrZbRvLh7mzqkamp>Ybc-ro*H05m|Tns&5sgjvUCVuNOAG zw#A}nVyg{zrAbCFP$#@?esX}T!B$fR&iN!P7nU&Vb$K8`Q`CBr`qchGyAXz|Ke*`h z2c-=lMy;u35knGUPPIRt3VuFITee05xt~NXVyk^$-j~yW-SlyALYU_eKfBFr9=AB@ zEDfyeYuap*7$1hTu(kAVxxWg2D#t8`w%kGDqVN^kC^hCNkF}ds{;Yy9JSnBV2ES8k zsJX?+P_^#&kfyUXJbT87#FHG(-16gB3hJOOb4~z(hmC>~I4IHFoK-`!J?=g1dI`~F zJCqjBz&q0e9iE98E_b&*+Lq#)#?-ffLSlMPBH($2d=~&-Ru#B2P8wK+zHjYcAMAdF zH{&+1jzzn5(X*}$(4dUrxauPhRGJjF&%J@+m@LPXtTWl!9ILcQGUbfZVkUwqIlw!w zT-V<4Kl{X9=1q3&Kek_AB7C>`xr5GbV%^_wGx;v1Z4`5UxsO3y7Y;@TFC7lESoA6s zz$-;*x}Pd+nF>xj4N-5ABh|~|d)vnSDmmO?^;R)FZ*B8_o@a=Q9$;NxuB+QUv!=kCEtE>)OX`~LiA&Ff;_=i-8FX(WP+ z^KG$}{a&e@% zjWy={B2she=2b^X3-x{;%BpVg-1T-J^FfraB?KCU^Hi{BU`SX+KD zueR|DakKjbuAV%5@%2_V5yfn0fzo)nPDg-QS6x!etaP~@B|McmXS{x=4@s3T1#HUg zATV55h=Y%d)x*cq($mb$EX7DMs24W|MP6wwt%D29?FG5*IckG9h!Wv!)Pl4jK_P4q zgsv&sqM%YtW#2xvtgOW&HKa&I>9I-XFY4KL);1>)O}$&w6+1f%RJ7HRT)^7}s=*;i zsSsFCa0Zc?V__jFmClJh!I=E})+hSw%J&cERsg9oNz=*@(o9=)n3_Z44qv%Wse14KZCvRcGOetdf$QzR-X_PheGN zO-)HjdqsWWnW&txuELMP)NL_|C7=od{7kK77R!hpP*~bS(d+~4IHwRFf}zzmM*NTD=6sb zkpDqP%cQn{V1`wTZFD{{^9vl)64h3so0&DKsN4dPwbp92P zvy-pEgL$lF7U#-Njt=)Xmq_raD6tXYa?(&OH&Nr`m8YktL>N-8u3x`Wx&PJ9`v1P> zFxZ$|E28#_F;F?OZSoOBjdkHLlVUi3X9PE92ac=%+1oiF#mB@Y!67Cj%#BM+ijtO* zk-@PrI9e3`0*+_@4QGqHOWWu5EsXt|7#PWMOia5R@qoxL`{7DNT#Z_2W@%`8vblRo z2bL4nVPIgPb;Q-woaN;iB6(P0n{O7uE?env*aavqPuNe0lo`!!czkm3%S2Q{B0xKU zt>;O*8rOdIP=}qfxvtiDQJ}-y8wUapn+OvVfQ!rY2ObFt1tp*l>J{hXKCPQmGOwr^ zt{GLxQxQ9h7!FSyC~X$BRWLwVb(GD?!p2%}uxP?$>-Zm$d3~Yaz73&|AC+e@&^K$8 zX$BMos&YHEaw8#f!eXan<=8aITo9<8*+3+;D z-9CY}L$HEOetdB^$Rr@R3nAH0bPAtNKx|%`+3xK6(QL?fvy50M+%hjRaKitWnZhbq zL!M-+uKthA{~t#TxRmn{pBA1m6Gc?<@dz?8%j4R%k%MA`_TJlOX%Mj`g9NX&SzKmv zBe-^=Gcd5QFfmd7W@~r%aC6hZ&fMx(Sb7FNtBJG%!Rj*-Vv>dH8 zaHViG@X}v=1}FMw$B_>i7#Kv9W(~pZ7>0(E?&RAUV5IW8XqS%WoZuCH0 zxb{h_)z;feB0(AVNSh?3(Li`xl4KCnaFP%v61G4R4QJ0o^Y>u+7~pw=@UV-a4VJYw zBCNHoiMUK?Z(w^dT2=Y@{D&^4m9T9r!R}Ft{ zNe&SPK=U#7>|E65(ku%B@fMNKj{3uQ7#RKDj#lDWT~}7!?YoJDP+bKCpDaG1Eh9CF zXFNQb=PWK%SU0cobdsrv#8|}m!hZ10(hOXIxTkjgpeJv%5tq8s|M2d=?rxt{sRL5N z^t#YCG)I5Sv+>=NxzjK@exK;tL*>6!;=}ZF@=`s&@p_qf4I;ljxEH4IQTk^aC=TA6 zKHweN-AaJqAI-CWY@}gh{a={0V*j5CX*mC@t+VHMKkTRw2wpM>2(1F{y?;EVe`GcO zNA=Es)(_-j{~vZB2N>hNWb!~fZHWaZ%aA1BGq0Unn7FFs4%11+L&by>t`AESeW-Gf30+Xwj}`^XWP zcO`Aj%>Q(~)31h&3lwKSl^=z8`KkGAOyW|8qkg(J*&k6Af4h?N-9z5}f8CM>db<16 z=eyfuyNU9G+Mge?IXr0ke$IQ@XIkZ#exYyn$+Uds6&fofO98aI*Tf|i zSo&--Zb)sqTUuI@JXaJCl6@&$yNWKq9)d(ohfJYX@;$iS9l`Pzk?xCER?AlExNOHK zYD1YuA6M;!NhE{vfpOk(E1x9&?RGdA5n_-d==C)c>HYqO76|p}N7z^;U#`b5xBrW} zw~URWTi7kzcAJ^)n3)qZGegYGjB(74nVC6eW@ctPF*7qWGsEro%$=*F(HYJCetcJ& zZmCs2TBX`myIZAt_OliVpD%<%58@PH)KSOT`3ac`79?w6_W%f@;%i7x&CSoRLVov` zqz2D{u1V35S+ch{In`$cz1VjIzJXdgzLGRA=Oi!=cXK^z@`k0{{+8BORs-{M!0+D> zrqMa4App#Wik$vMwWY0vg{oCpes_6#)JlSn)=c%kdq}u)idqXBD?8sQsveq})CAGO z4Ok$YvVVN9fIQ5twaV^S@juufjh1WW29!2Jss)S#j;?oKn^KywJSDb37SrRCi;LcQ zkWfTQv_KMUI_P5BNL+s0IB0fhZe~G_wX@Ufz2(K-1BgnECiwW1Nmtvb(tPq%4`o6o z@A#C68UqR%J_tVXqH91tKNJ0;}cl_^#3sxJm@^rfLzH{4*Q0= zgWBZ;RTXyO^^Ujj5iMAt+q4Lf;sZE0pUeL~rqi}ksF@S;174;)zud*Z&cxcm-OT2n zA`Rr?&CJf*S|r3>-8msc(L^NL87zdOvE%A!RhG9O3r}}VLtR_b!^!*#D0Z9aTN`7= zp}l)_>Y{I4S|=M%^3gP^TzHts`sia^O3l>Q(bLu5Tv}RN(*X5pboDZzbEb2J^*I=z zlDm*2Ab;=$HP$q7x4puNjLzo=n$M{$J>dJ1Y z435j_=oopq=$M#|E-vDnd5V_UdX2_eaSZpCrnK57_tv?HTbXjG!>7GF(B10Y-QE)c z*w3y!_|(5z=?Rv*($qAMLNi*jL)v`rb#vRi>8mD(Pj_c;;ET`8G+^g8Kc_(RYdy08 z2fb5GIH@?QfQ3bo_|D+xv=*LryRV~zyZ80S%?-$z5#b2FvI*iI+eh7#(ps&QD4{U_ z&^(G0^_$wQY?S{sv8nIwMNnYUg40ER5rR?{XPRs9DZxz_j!>Fjy@7@z{xX` z#G5bGO_(?nk++vySm;eB^MhdZ*?PHw^!+*^cOq#2i+K3g}w225e$@b$B4N4hH|^e~X_ z4dP;XSGmw}u?y~>y<2a8?ss1wt9?9!;d_nHFd2R_!ZfO6vD+ZK8+J2hMHR< z9XJ@Q{x3XS!YD>7c0xm!>=!tJ8IAF|gH&(@ij7zqhw?ekc*%e~_-Ugt<9K$3@7m{D zVKVwZ7>L{_zP29IUi42}v`vwK*gm@kuHxX!_TGKu+yAJ z4o&*3a!ir|kem%x5gseoT6>gGY#U=EIx?ib)>%sca(pa@&OlTtWowi;9#*m{35sIjwzPjdgJn3Q_ELz_v zP3?Hve%)RofI=qHtUm?_SPaIESJ&IBg*wPRNB|FO*BAAoj!7A7Gw2emz;m}f;?T7INX(i|^SG(&H?7G883I2o6F-xPxk@4i+ zoP$L_F#PUx$drLMu2tmllgMZHL8cx14>di!gtCSMlg{fw)<_JlI;X2_%Dd6xLs*MD z_NsXc_f-Oz#f*!J9qR^2G5E{k1Xze69^PI-Mr1pOw(5;qh~%o}7yE|avXw+aSnvq7 zFYN0z+e)Vg{0mRyE7^GoZL8v=NLEY_1k@JLV;+V^d&$NxOESv{YMUk28rw62FhY=G z2aPYtu2W&hR$TZ6Hz8x#cN-0dE;!Pzf1GM-Tve^vHF`>QUSmI&&S=J7k!37B!Qakf?ARh zN3FtUma7M4jY$~A%NK6NMxG$agxvYk>>(UCmOz*GdYJ%#A7_Zg%cv;uTwTBlnnrj*+goi#Ld)1Vgx0o4BD<;Vafwwi7Ysq z-0|2Fb`P8LbmrVTx76H78#$Mr&q4_&`aYUbL-YF_JZ&u+R!3Uq#dO)d0yaem+i*>>cp>#4<4Ifq;$r436WQv{cc}sZ91`%B#hj zH%eG_*gT0J*_hSRk)RTBMb)UkM0m1qN>6?#gfAWSjo_<144! z85EZTqwfAbX#g`cTF8QnRARQZYmxm!(0-6E(gs0NbAbb|12>FMQKy5q2B5I|F(W&D zSAIf%0=bM>_R7su&ZP26Y(jpE^!)1~RaDXy{UMcI+^^)@gaK*S-?!*3lI^3V-B)`aM~Pniaj`fUV!WVqhl$u-P?r$c6Us-f{GU);q{(O$tHLqCmi+n7e!u$7ym1q!Z;@^ zOD=>sLb!_(`m|EN&{pQ-RZchZ>1IXe0@oV&f{MVr2G7`+v0l_5@{v_nMb?wZvX7V& zq>ZvqW21HEa!J_mz0f-5#K8QGUDoLgezXwmwVarO9cwuvSR;MPpPp1+q-n(BevB`JsAY94Dh-DTdt#Ykk z9HVl4u`Vhycel`lB_&LkqzfRC;*C>8brXUw*mN;>SG5>|uv5;i zJu=5@>Wfz|t_hSX3cX_~^|mkyu(6nBdr9>vC(ggMu!^MDP;Qp)@dgdFch3mRg+`z_Y;H6FY0qYS+yh*?P~=Xd!! zkX5XfnMA%F6*2L)&c1B+cV!Ee!0*LO{I2{Eqbw}=>KWtkB>bzKn@%+JOnwGdI$6U+ zGQhTw-I2ppNoxarV%y*XH)X71_ak$Fcl9_7n9$UniNalg>&U`o(h_}`Zs+ozgQ_Q6 zt}Vhjkur+%Er>qQ|?+N1bciD}0a+oN^5>%}6sr$_f)h7b8dizIs z<@`4$s>E1OBm+u~rrmuFaCKz? zwE1bO#_v}S@2R997|c@52+v7*IfSZ^zOIX(&?Il4NbUa3-63C=MNtI08NHMmjlPyA z*0r9Z_JQjVIU&{rDJ#yJ#35u)uGhpB+EkvTZWw>}Lk@Kw6>Wfxw~!;`q0z zTtV+h^wAf_e9TgwpxAhc_)SwkSi)sWs-is8?8_Y0@)s70oN6RWy63aGZ)}E=G(<(C zIy|0Knb|X_yeS;Hcy0{CBQOFO6DH{4KM`#hRQP!b4|mu{Y_k0KW(qpd+7a-M^1yed zUB|7Db+1B0leiUl?fMRiQygrUv|vcX1P&(4cC@)ssp-EdmX2#w*QIWKfpt16M>642 z4jC*%>{sU29~iI?eRcXh(I|7DOou#g5E)vvW#@Qr-GXW5rl==dG6@g51>W%5K~>j# zR7t|ESH%ND^Dg2bjC8VVe9Mw|3TUl?jwrHk8IChejd1PgMT78jDk)B2M@lEKtubMx zPpy;CmH(!kuIZ&0SqzmhkW2e`61u`jRre=fw=?a^Z1_THDx@`zf@QMWb%YI>)W1Vw z^zGX+q88ufuK;gwaj81V{KM!31<06S;y)Hi>`e#aFGJ+(-8}9lRwlznhpeUT$T%D^ z2fbo@LPzuV^z;oLy2Ih|W0Z2CTMqQ0-_Sofe_=4>{JHx!>pL*%8Re`*NGDWx97j5h z$PflyEA0}=6e>li&G<%z>d`=kNlDuFw=mxY1wonraYX`LvK(~BWnGfkkl)uJi6SZp zM}?-*&FwQ4WkYLgPcz_SiGX&jr$(@V5aRCYkXEGi9q_mC4b%K!LfDKa5!VCX*otYm zY@^Ui^T0ok6|~UuBVhDEy$JF3Ho#?sCW8P&A{IWJvI!s29|<(mL&GES8vXK_VW<&4 zb_$wmP#ffg7rv(n0wa(HwxJ*Uxao@q?M<1 z;4L|b78bHz8%kQ>>$_q`m{jcl_lH8&t0{)M{!R?lSn& zklpP$el#mBERP@wM}i%=7M7uf5Jg<8!JD0yPuXX&#K?LP?Z!$x-M;FNf~=}QYh=Jq zX*T+1LjYl-s^KuOur4C+o7AA;@TGGCTxNm;I%Aeh0AVGFuzAq$3;&{;W*D78Tzi4c z=@o-DQn4;d9=!e5OYtN#fY43{zE|A)xFoEGf`KLGchB^UYCD1VPyo-tHW!r6(b?Pk zen?D<6MpA-!%S|m(!C1F9Z=(IwKN5WS*~0yCSru44J<&nqx;YOhyR(~m(I-7M0|$R zRkS3Hh?;n;2!E8BU@&ezf$kn>Z%R8)2x$>{NaY|1x@p~+!u5w#A;}j4j}TiB`bK`z zE;B2ZpeK$X8VO1Wdd&*e2rpMELt{NO5jQt~ud}Bp=ZYN?n^lsq zyn$qZf@S}AD6OgvVIO?ojV=!tr@M>wRgu2hF#X|&!VFsVmL~jyAM^wHDk_hF2A`|o z#qHjYuiF)NJBuleEV0y1l!||I2Yau-p3el&SK%W91@W|3+XVxE_}H3jZ;fG#JhsPw zVI!8@DE+H5IxnAQ7!mHPXM28jb^)Tw{OjT0^DaIY3WBlnh1NZJ?vZ-05q|f+#PxS9 zSc{)txAzMR4-X3qr-*)J^(3gUyXS4u_EN7KF7Ce(gwxm=$)*9FTANFM8f!nSoZalR zphv=7*qtO4Da^l8NYav6#@_Aw#78(}9|~We>RVBwB6w8ex>WDpslyL<6X;y}Plqx4xYnv>xA# z9gJ;qq=a=k$?cHx0bM&w^V*55u09a4?T?DR*)JQy#kI4shJXNj3w}ivz59Z9z=Y94 zQ?d(iXlm(cZ_dfy%&bTQn07tAE!4q>X7;*n?~xHq{5<9#%QimsYhMQ53nHLjkd=um z=*K@OFO!m~O&QJ$uadLFg5j9<_ws_jFeUlr#m(J}KvCn}mH|*$4vlU{yG&>fhQU6y z^ugL}bfo8E`zdG$6KjWvjEDvG!poytu2?N{02`mc4e%f4ZWo#s%w-HMS2%)Bt3qP> z-FcGmtn%81+NpOBg&?iKm*chF?TXd<_n;q29tB$S5 z$_`rg(0vAy*IyhTpPwHeUz`BBDv_e`uv~2ISZPO=d0#RBb5p={R7G=jb#-&|Lzz5F z_FsLe4_?z`NwI{D#BlRFjenpN=4PNI9U9@&27=wcz}sCcYcV!JkxVKOP+I{d7%G3Z zXleJz8uv%w$g!1Yu}ajlQC7F{QHc4MlxkK{iYT8hmpXZ5K#@F;Eu=t`hkp6;#h#6| zgP5an5<$-S!Dl$8`zEPA1z#R*D)6TtqzZ2W}HK%4t;pT~}YmBoBPcip6x`yjmbA;;;P zB_p+3o%}-c;G#v*>$7;ZM4Q*-C7|Ta9rR>F=kye#BwGAFu3Twgwdew2Px`cqaL4Vb zmYfJw4L(}6MOSfVYCQE2XM#bv%VL_Mi~pIum`@o)_t%7U>hNBn@mRZH$c?JgoM0lu z9(ImWOCMqArUW4szFxM%>v_{;jHmSwToBF`9<4;)G5Aay-8gt84h*mR-n4J!29XSh zH(W=-gFnFWkQ@v)2WvPuk{_d{uq71XrXuEi?|Cu>+H&9}2p+qOZ0q{X4K4>V+)pmp zb}Zo07Xew2(pnh1Z+Q52@lGJtZ3vELJEd zmG={jFgAZ7?48gK);A;qJIV-0knck`Wl!(vE0nb-aT}DUk3NjuLFtR+S)q)8iQTaW z8bVYm1fvm~YPVzdw#>9*UOWJGCig=P!tNr|jXJzSzYA^qW}iq245w4}>6_iRKsToz zbF@Oi^_;YbhT+fG@R$rY<@cb=jD>RbPe@Q3v~ROqz3Pn^3oe6~{pR-gRtmX(hhBOk>}d504XqhF`kvzs}pXR@jDo!E^e^ea8&$UxNim zg5kFR_V8YDaqlDmEtIhHBpSP6LdUY1im58qdpxlvJMDK5#A=?_Z-(hiXp8}_VQx=MYg9ggqC-`X5xsJuAY5CSqy4Gh`S68Wy&d*!dbx8rh za|f8QfLCfck(BLr*|-sK;qUek7Rk_@x1}eB4^thI=ckuMIIK8FmzSrl=X+`JIW|J7 zV=IlQ!l~#PSv$aijV=Ttl!u26B%E!qF|eOVeiNNsG{RXlv}Dc}uC9L!TwU#Qh_JT6 zYjB2$HNYy8_;#ibhu?Y(L_t_WR!$gL9PvQkKY$fwpy(3Oy9DwVHU>CRRKm;UhXW?4v|Qa zfEoZW>4)wR*l&ncMM%oB$spd6jXPF$ekLY1Hm}DEgzXDfaz=h`a4QDj6nKT*aM{iq zi;An!MuxY6ovYK?gh{Xh&Sm6m(Tm5n^7nF*fK`xg z*3vUOdH9a!Mhs~H(mCJ?jCn(?sHA#=ntwPc5(6N-_^xQ$5`_&a>*_4|e|+RR5{VGj z6O7+sseB`YyxsG<=?y^y6O#heKfn2lU>V&)(o#r{%{J@POB-!v+{E5nV2JPxf$6$2&iN0A>XMkBcq7R`U+N1z=u7% zdM1N9a#3BW=kK;qHS~3~w4mr27{1aA0*HTUk@#Xwg%kwwDnM3XE3z>8^&C@7wT2*4 z6QNO=jFecBFpm0Bp@Ng$){e9XK8;71h4G07IH9`1BS`B@lIykT?z{R*AV9Y-eAgTZfQG%_=%CtxlI_=8yx;;71RxD+WLhxPy4HxGjfR<|o(Ca}{P@X3g=^q3|ZN{8o}E+yFpdoSq6q zR0L$?ByfI(Y1UpH3D93s5?aP+#O)33qc1wV--KtGJLVI~s#tsWzp`)WOYPOwNyt_0 zbiB!h>!Q*>a%X+2e0!(|X;@D=-yn;Qr zy#(8KC0$&6ogDjtu^a$3KTViO9{9bS;HYFT3g(zt^YSZ=twUkLM)HH<*HQKA2bgNT zPS4u*dPLJ+2m>%lEovaKzvf&3CL;^Wk01yZ7*^y0JZ;z+bzxetipsBa6|OL%hF)V_ zj1IPt-Wq|9xZ$+lO7v<|e|;u8`9F3pR&RDdcDUWX+;5a)x921+j1Wu0mN=!tn;Q1_ zudP8~BEg40+!a=ylg_9MN|&}lV>lRKH~U8$QvFE{S%jWYh*qNo*jJ#a>CjZk(`LzrzV&b`uthjt7*PJ=8zQ)%S_?OJ*h7ZtH}Q_U z@2LAzU@(3_o3g`5{?oa7IjyMM~U*lKiSE|gE zXeNI^reqKV!!KDDNp}DyNvDa4gDA)&#Eoc``3e45a%K3WwIDH{KXMx@(eBgkbMV$!#X~1hW$v1EA7Cbz$n2(xBf=x_Q=s} zL3n<|mnJ+L3s`i8s!AenbOtH5f-wTEaC1yinv!c#jqrqMK2?kKJOyYs>-c`%;#@bD zf?d=$^-Gg0zi<4o2*Z*-5b*h!-?|0$0f~KMh>okF69HmZX^=uJOJs+VnR~ispXd}? zJ+xpQt3J2fY`!5{&@0@W`vQLi${L2u|DNW&f$*i->QO|k=Bb1GvH>T(yvM-HZmzc% zI`XTCRseG^m>2*zhZG=)g4pw@$;{{+{^*;iLADZle+7ZCHBN{C5cAy^6a{EQ5(8?W z_Si$C{XOwo{MY+dx%Q2NM7oPF6!(k}`6Irlm#H@y53#g!9QaIbH4BzWZN&~>`3hSg zLI37LJ;9Xpmqf1+g|DTpBCeu@>Y>%3v8y3^oBi-u!3`eP;EtF_E~aEi?|! zcIy--Jljb9+>H_VknP#M?EZYfqb`JJM5o3ThM|Y01tX$!WcHzsoyzL*2*&yrnq>AD zpBQ3k*$nlMDGI*94H5+4SpsqhJXV|75WXVk@Fp4emDTIe+m76BGG*|Ki~90}%I9&y z5bMHU0SW`~9AF}B3L*)l%+^*N-~qPVIF68ME)^N)p17e9s-`2<)L@I~VUl8M|Igcx zvh1~Nvr4*s{jL?~cQXzSXM^Xt_dYaQ&xIC}Rmgb}36qaW7*|wa53wMs0#rkwSx)s^ z-&?MoM1@;}4;%yU)+`l=?_wlCTL%*Op$LX}NAfxS#&X}>!!MsUNpm6f%;&YV*uvt> zVOI@Dw8pa4FDZL3zVAaSh?EG3!u%r)Oa${Y6~bKNxY8|(*+e@aTlqv`IHzR-i1kz= z?h<_nF!3`7Vgkkmlm#4pS{n~H*;XZ%iVtO;5j^sDL!TXE4wBUt{_xGNzan$Ze_FDB z+%M|;X`llr0HDJKUM&F6co6QM;&=&-B3%*fQ3P3iA;bjdd{5;KK9MiTK4K7&AMWvG z-;49k+YgnWjnQZ8A5m|&9~(Ty-uj7B&yVgEI!l13NO_vz2Kb2xy_6?k1fui>^X)`aV990Ge5u=8c_jH*Ku^QFuFKH- z^TKOuqa%x!H13lPrLzEIK$M|JrU_)O+c~aESd%a-qF&dMq)MuCfL{vguLpW8ghm@fL#6 zJ;Y~OxMD<3%rT%o+da_mK0g_Cd{>R@dpimA1qVPXsC0J-X!(49cQ(===xXJ!-$3d6 zK(JaD8{kvtH~u)>dW>p|*$WTy$56tO$sQyUV{N6P`78qn-~;cg^IzOpi?;-Z)(&e0 zIDPnsk~_KgJA za=+Orf7wdwJXRXgkjugpA!6gBm?8Rs} zrp{b&v=ike?z!M2_DT7}kw5)fZovwJ=UAN_Q7Tsmd?yQr?}Fw0^2cU?st?Q?t%t*p zuGz*BLi22R{&Q!IoyWG2-fylxO?X;Tg=DJvxt6doUUEUT6a%`tJflL5LN1* z4C+m?Ha#mmo4keW^Vj?6-1>1tYj)ILjIP(rHJW-|+de5c_~8=+H&5&$A0*pjP6?pA zjtSQsdqcvBvWUR5lisX7o7cNMW54|;&wh8jGE|!GT)}?0t`RtQAHBN_0%wLC>JQ+GkTsoud01(ZkxUd{w=V}zj|7R z_sl9?GAD7beT6K+YrM7D{QR>U84|Q8R+=~skm1{lSB00-yvtAU!oI%0@Zhp^!j3ux zMGnYggVD%!ckL|AlF(f`>{mj@)7>M)%WRq+mxF$MbGJmk?x_%1J&n>{?>8PMiFs&) zU+okx3vsjDa9td3kaKrkP)6hh-xdmqZm--rAK_Ff}ORj2>kz!1y%H%#;z)dEwXUnjmusuql z%W^o*8LWY72*#8MQR)1}={#atsmHn7Ls^`rJN&MQ+gAAeoBf{uNrz4dNv@Cr6aa9mcka(Mlfl;NFCb-b8y$eGv%=p#0@D zK!ivv3cz{a`pGg14sl=nS`^0quTA^~rnIw*w6ZDE3;qdxVMQIh26VIvRvY z%{W}P5>oKkeJFBfudTIggu7Y>5q6gFMn4ESg}_QZ!+sIr@l1WieQlp#Py6nQ-`Y6Z zpFKl#+sy?)eqn*A0o7>%XMml(L2mTKp7H(1xdn=a-skX@6|8c=yG?F=ROa*%b^u^* zzwcKFkfu#5@5HS{U&YJ2W0#eon__(y<|$prB^gif%wf7hY|L-6kpA+A2^n-E@IVBc zS^F9rn|0SuF&VCh9OjT@si%A3!=eFx23$JUMk_bL*4hT=;yG>NVW=CQm<><>c=XlL zr{Zx>YM$%L3+8ubtM+rbw9GV?Iqg*Hfp$b*nztf#tqV4@(GrB1nVJq3rYe5XzIo|5 zQ9W&{dR6&cd#R(OI<>3EP#E%q@l#kp@|Z!OUt^-HwgQS6bDmj<3*0)%{P=ElRb;S!a@8o5XTN(J2W==Kps*qQj)NP zbG=NRWrfS@^|Lq}!8^c#pzOIj5XS2~V^3&offF8`_nZ)k_JD21vYuW9=ZGeN1c}Z# zJPd70B?FdiXAOqaQ0_SvdxZCw1GYq4xA46fvH_5D68F?8%{+9}*HZR&%s5)+eMxQB z;0#9YQ^V2x%ib_0NUzuZgpDqjws?S_3}h^BRP|42P2jvO`-bc$A?=d7Za%pN7!e^h zmu*GvX{D3RtgJ9f{OOFzl_fXH^_97LdY5tVpqU4Q$itnra+2wav43brD~vI(VK^t{ zL}Z9(Sy2t!*ZDC7e~EK=@S1F#sZQS~u5_a43H){$V|QGs$BFEl&z2V?27ppyStu>f zWZeLKyy(d&Dm}QvSh*7y?714wuf?b*I1Q!l&^p|i&=*%Ny}5zLY@*iqV9rd#zEuWR zr@35!7!0;Y92Yd1OTwrl0}EL)beZ-t{~(w%fs=~~@&g_Aek*Ix>jKg^*rnO}C$te< z$_j zpzIg@1w<$d7O2ZKP*MV(Vzt;PfWPw_lDf))z$I_uEP!dTy7^n@If zE(Vfs+q&|)07McLkv9z(5kNk@jzXuUTAbr(n)l=s*FAgxk6t&1`UG5U4gK1-Fcvgmk~?v}o9 z+QKpCE}PpYy=XnqYZ^WG@``{48WmyTp=tFAL9*Z*ZsLn0d~?6--TE6bD?k@g9g1X# z>5TAs@JW;pW6js%#Xz@`!CFXV#I_cwWFIh+!(9d-%yuYcHZ&Vpb-KZdx2;TUD6Vg2 zKnWbeC?NiaWqWqOQ2<0oV@WcPZCk^@SmIcAKjC*S13e`kIwwC-K=dTa3LEDjPnCA* zSsn5kKndB-!FIhXYJ~xCQNer=mumkLFOq+4P1D;PQ;BxeN==m#Fd!(&+KU}utgA7j zu$7GJfp*ynP=R<-!?}lax7XHg{>pH=AVwhVOqi>XYFKM^x zGUGWtI`c{R2{};77!Oa>BNkPgb8lA-&!ejhSv8xyqWv9(8Mdbgzs>0sIi5<%eC_W? z_<2S3zCbgA!b~2}sgwM4rTn{WTOplKxx##R5IH%(U9hDS0bFugcltbg;O+X{Qe}Gf z&Fh5WX!O{0e zrJIKU{3W2R@}8`Xkl_$Wv%OtkL}^y zAebc}b;@v0$$rQ^)OvlfFk7Z9gIKRPe~w;sYy8khO_oY12^@`^@0)M>#x+d|mH_?< zp7Go^K&!$C!0Z(@u!dI2HL$@mcK(c0WT<1ojdiKMIwe&SlC=&4lK&ygDSH=)*^Uqb zh;vv%|9RBvBl0no>;hMo`Ftqv#y|ebYe`@a=GSdapt$O@T@XA-_W22(KYu{`2a0GE2tdtUjq-4psq#;NdB+-J;P_U>&o0y4-g-~LNVd62x6cdRN z%m@}=7dQBu%U~y#Qm7Zot8T%P^-mV6AGlt#%BtF2Hu-4^=!kW}j^QNL1>3-9Fk`EH zognYF;m$Tg^&hC&&(DTS#u8qh9ejN2>pH%c-|8tCq(*E53cl~F6!5aK)i>0E<+6aeKjYuuLu%v* zqhb`OK*4{JHdTlfM#Vt^nFl@Q1|t|Vg)EItEzL%5lGJa0NISrj-psAd45*0*66)~} z_7uY?K@lOtA|k@WBgdeyg7~(5!XhHk&~yP9Tf7Im`$CD#+ITXK%5{W4+b-4$5+YMr zk$Qhd`UIq}4B=<&i5{I2l_)@kfTo40xR}J?pMe2b{MuTk>1o9{)a~5dUTLwM?r(}C znfNBYth>a9y!tFM4$SfEfN5~I6VR$2q&`5<4d+vyk?{1%K4-;F>#Mv6@=+<64eM*0%PD?-{U36MK-VE5kw;!RauJSDwPcN z_8W=48cJyi{Ii#+;;iK^Zf2(T3klmC62`matYQpg{9|?gpYlW#*HGpudHAq2R$(bZ zPG3z23Go{h46=GW;XCewY8a%5$S`$^;*!}(6SKp;e+V@gSeTgbU&O?MAR(XKTDg`} z&1dFmhRdE(hH4CgKAFB1fsV#A1hzCiZrC+8eEJWBe zI;{M{Mgq|A;%Z@~if5QXK$QsTi6m7OHP!5H1!ZMrZEX+5oE`<7tG?%xvXSsQM+&j$ zRxVu2;&?P8GT8XUhWaMf-MXX-RrUJ%>@rklB_>dw((XnGVWg5lE9OPX)11@NP*Hc{ zDf@H1aP-S~Qn~smWMsK|wFPVPxB2;njOsFaI)<+xRdK$Y;$Jbao0Q`8`hZGI)AC-! zGD+RY@8T+C!xm(Y4)=H0RDb>?HjLFjixF2;m9`hOYHd!rR+g4(;xV*;Sy4<&Ppe{M zpVOb$DJw69GFALo9&1Px^}l<(c5?1x9S#%~JHbF>CRLRZhaXXKArcU?AV-<4cYv41 z$t~*I)&lXIe`tF^?*tnNGX$;S^zc3o%8e0-5tpk)sm92 z4#1Q!@lf$4m#w-<3MNd}C-DzMnM@M7+FXg1`}5FaOWec`Y0JN%8UBzs9M9V5>iX~# z3SR|>#Ipv;9IKlT&CDg}6!j{qc-0d_oMc0hM4H@NI7f&Ju2HRiIbo0=mPx9rkeS@w zs0NA-P?fSc!cVGZRnM}rHvV#QetbaOApFwB;*W#LXwkvbFVYPC>fYYYt(E?T)%lQa z67#^UEMbRC1mj9gPV#pJHym0dy{hb3r9#yA0i5|#7IS!MKi9(A*2?-##5>KVu^!&$El8p(%sU%8Q96PnM0`F@yb_DoK~WMHkH-{ zAuy6^w(eGDq}*?521JeG;N)dy=jUSMV{yFP@DkeH=uG{`<9J;gx82*7RrP=+sFs|( zXI`8c80&`;Uy&4_wBq|M4M`KJwJf!;65{*305!dMQd%6y7wW7yy99DM@-VXgLw0er z{q?Zj&(*uT-M_r-!2;^5lZild>Jl(SrA4Tkin-yIqA(8;Eu0uc90$a!Qteh-03tQY z+6-QhqeDSpXYry1iw|p?%QiMnUT%IimREy|>D}k&XR6R*aLA}25+Z>#4@M9N7=*TR zEidnUmX}4WW8+}}k%e)heBqNTEFhdxkgPFTpmmaDRK=*j-+#BU*L2qPQr1?#+`3v1 zJAOqzAQW(GtGPPkGE%I`na*OM=k>aAz36>E4r}iZ=+ylwd+$7N`nT2GpfY!lTT+da z4o(T0EKRiw|C~lO$*?x;q{1`4wzjd}+~DbAZ;T!WJB#7#SB7uZF!-aRG7%AzSb|(U zEN(8YImPvjae-r#N>l^!r!AvQVs-wa#kYmgNz1hQ(S#DMD&sWdxamA?ufgI}zMROy zTa{H+u!R56iAQrXp!fb9Dd@XMu)u1j^i!NFB6ks|)_VKd_d75;wzwKOL<*N3Br)PD zMw0E$2DY>`fOp;B-zS4Z2kZQ!ywoBeI=)6tBuB3!M;`tXL=%s>hg8(hl9r-fp9-;G zB0WC#tte_pA{H|&V zKeMtXT3m}pwoFSuxIDh9k4u(`ZE?V2tS#uzNptl+S07kflmCC z%XkFh`!1>_XMf}MW@KSvURrhQ+`f6P^S5?ZvTm8#TphAg*VoWqRCr9z&vRc6dE{%kQkOa`<}I-n|X4oz1Q1cBTKc%e*lHot9&b zl)TyHu(&pSXfFCgSJAiOyS4a{mGW@{J_wfqse!+k!ockiWFmezd>l}{-UZlNCn+4g ziFyVs6t)Q}?8m?c15WviiiO-NI0)GDdEMRqMVo|cK;5VE zE5lemamcG}lkThzHQkVsvi8O4{t0zGDS{@O@7Y$mvEdf-<)*Lw^YWVrbLPbZr7WT>VVJ?)t@xmHBoq&G9ahh3OgbU_ z=#MH^=ew!1A)Lih{AR`rev+u1LX${a{c6X^6cP{aw5qv8YzK65;|BImY0x!@ympdP z6axuk|Aa2voBrgw*MuS{8NEHX z(l$hr7ff-7XI;-yZKHCLlp5-qJ3?QUw;yo1_qJd&3rn8tdrL~V(}#Ftc4pM$Eke9G zdKYNZSq(o%UX!^v2JuU%SM6ZO`1mjmdc*c8-+VfX^G@aJLpU+dkd==QawTD(rrX|a z5N37=4BEt9V{DOKa6d>6%D4D=M&L=Vkx#B?<9DumRcXtU60fM^)3GVLh1a}SY?(PIa@Zc8Qom0Hh-Ea4EzR~^T zoN>M}@&`7x*;Tbx?UH+~Ip;-NN=PxqP3{)G&E2-gzCX3NC$tKjKh7 zS?%#ElSSr+bgS)~J{S8ZSTAa z$qs)JU9YuJU_zt2SWkv z_v={A+Hg-DD<+nosP_-s<4$57lYA847H;8ljDG}p5pj_WwEIA#$EOjXp7zx$5Z%mf z1ocDd)?37K-HIEE#5bW8XxuiDlHAL+54(!BC@gkb$C9qvMw`&ShjlTzry2AD=OnTC zE%fE&HT3h2Rs3?PYT~2oMJ$~j35CV`&S#DDOb{vxH3|h-_JM|~O3Vu^rPG)XGRkUb z6#ao|QcHcL%X?|kBh(FFNmr^eyxRLBMnbl+5oj30~e1&Kfsn%jRG&W|s&8 zf}97h|EN{}WBIz-4)zLe5!wy@m*xIni^IA92csOUEI^6>PjUGF6EcZ4%yQ$vwGwU3MpU5{^4myZ)$11jJobvos@=_%K8I$vO zY7Qyo*{ZxJWAB3WN!1;mvECD{xnL+=r%ZI={1MdU>K8zVIoO3-Hw-I zDDN3IH`20nxC$x!Xqfsyj369F%&20H`#PUAuhdrZ5<($|kDu z@YdGBm!tan`i3dHT(Y8dhwtna*u{qlJi8^olWka_@(>W{tk%(8^ddbKlRGxzrAB;^P z2|2@ChGi5ii5V&ecXbzn{u~?}EFmE=I5RwLi*7{s+7dVSA%|ph%t3oX4&*_MvNyX85k-zVX?5V%u+w>ZtsIc0$_E_-6G0B#Z$V&yDN*sA?mF@w3RXB z3$p7}5s&#sXQXHroaUC6u^oG4WCaBUMrMc$0QD>+b7E7uzQ3)HO!7%2SR(jCpd*?$ zyfBTvypL+<-XtEt$;n2X`8y|P;-8$HwEx7(2?jVhxd1087ju@_DZUSZ)#EPL^Y5IT z>^Dx%`oD5=(Epv2L+$<#PL7bdVSUItNyJx((EA;XrH~WNE8V|wa(b!$D<_BgKXP(r z-Z(ih)^D7g(nzOindykI9CuiIu!0v*ZzyU#T09TQ^~;O%OX&9M(BYD=3k%J65Mp^; z{>owh%E=M=CnrZSU5+(`WEhL-?%?d{jgyo8*PNWo1?YGw1qE4ISvhHWnb4T1uy7c7 zSk*iCkyRkwt(-nAXHE0UkS)Fs%osBAC0F5r%$sn{8?+}ERS776MoKMkQWv?oTiouo zwYheCOyW{f(i1Y1QnQld6H&0x-ysDG=-OjZdEiCoR4VpcN~WG_Ko5crKR};5INx3= zK?F&N4N*`Qo0#rw?_g0!gh$BA$f_BJeg7W6xtS+oBJ6D=Z|ZJoFG5Fhb(!2Y>gw-^ zuo)-T0a5Dj!y^ryv*vb%eC816aSs_BC^s@XB|tD8le6SDSS_s{Q+9mP;^JJ^S5UAc#>DQa=5RK=njILB`va zQ&RsouwrFrZcS-vX-i9sID{WtS{g3i;cv$IkLt82tGrAeg`$TwAdncfbOa;Y2L0cZ}&G;b5pa{5mj;cT{vcKt^US)L_I!MudHv- zv9Y$WEQju^jTa&QARQV8V*qgsd=(HNVarmDzk(>(gRwC;HKw$=eP8huH1#LhvWCSFV-~p7x;pufDA3X(^ zpFe0*w0x^Y)V^jT`fv2WF}kF7G(eJCsk`R%Lr^l&C*t3G6c5(vebutf^+DYY|?h=rdW zTFvHuFoD#<&Chc7!VW`Vqo%|=C#RXScHL`JTDP0P~JXm4ffIRA&Muq>c=ip>VwAjUqKCp8$_^11a%knmBw)sH~DeB{*#xB zM}V8ZsoK%w{$%vJ&!(Qo1<$y{t94v2rbUCM>-UUe@KLva+H|(6ySR%+<~Z zNbDX3;_2|kxWN2VaIMB7E?DC8zQE<7YNZ;*&xjKVR7WTxb5|7h(JUab){~OcFq0ue zuFN?%nAu$oUaf2%w|)D4%SkA(w6fwE=}PLK!xuaGhP4T_GXO%Qn_oY9Sa~JMq-LT; zNE~hSHlaF&5Q7+o3M22*EKs4)bA1z1Q~R|v!_UOS%&KAdT64{hjvG@bE6)+souruIx0INg-SC2Z!^uR>m4=v~Kiscm_jD4kFrr(t_ zd{@6hil(prI(#?LqBZvIclYhpVgwdrQ=@Je6#%Hw1pqf!BfEpo4Q&NIzXSLQV)YM# z#tXhygS@SdFw|)dObmTDMu>{D(*IIc%9mf%D4{OGrP~($uKQ#ny~!|~(^j_&sLKat zhmeU@+S=Fz`I(od$ThwupkWb8d0|&!EReV=;;kK!U>hD8V}tc6_KdPn3gP?0H>rpI zib$yfvGwqH>s?zIly#atb4Le$O0DOzQXLPf@K5$t7%D@>$8H*c2h~LYI zqkkPHQg-4!O_dQ?Mw(8mN6ejdhc~=4k$|Vz6J2nQm;ew#=l!^vuFeP`fSNnq%?uu( zq^oN_IgyaXAqjJCpcHVlpAt*K6;MVJ8vY9=r{xWkQOGyZ&#O94-h=te(11T;C-g^WHhpJ2FMJC`=YRFZ-MM)$6FC zDZNgpu57{Q3(e2xW5wBmoxh-ZiepX^AgF?s`kda0bF&TK4Ku^086*~Qo4!h?y+=`r z%nd7~cMtWEa#S`I*0gr;n3}$uxL+pk#;OV>I`g`}*ik=+sJ*|yf%( z47XETPc0)oI0RL1UOe&|^lZ)#gjq6sX22mdiGIx(GH@U#WEypWHxf=)cX`3H^V%Kn z0lf29KW3Nr{SywZF`BlXW?FiyfE{9p{(DMcN7Tsg^RAdPTTngESvv~>FmlY==!4jq z@hTchKBaZGiE3wkROH<;>$kKqr!Gnm*$8Y>XrA|umpzBaK?mo#C$w_xWlspWg{6o_ z(Hqb`;2`!!IjKqfMcDI!r6G>j;OpN8H$N@~F)_^*&xAmzlhBz4qftdSS&V5+k>>fkjCu!oS@T^@{ap6)|D zfp@>+|6G7Sb3u7}3bXu5;2*(?-q%4;fi(uta^M99J$;7Xq4I3->nHOS^@mf#NG7OO zR}g(ZIc8uKI>2a7l<>2_GO2I&0m#bp+E5bVt;6ZAFo=&q2(g5)V;mltm%7&PQHy9$ zAzTPKghlcSOycx>9lmDf>Vnn-ncHbDIX{GT4gx<95JFqet66v$obK$>2oPe*o)INb zeY&h*Zr{BFrEMnQsIdAT+!wOTu??UIeekPV^F}j__dt97Sc^2NVNRP&hyjv>!h}SP>*tI&E#p9E6EXPi;9;omp`` z8w3MZFbnwAPp%Vo3$*h>9RveWFggd~jM^u6pWKJHJO8Quwbg^dSMr(mzU|%Jm$_6`^dGOU6I|n&% zk!kBfE44n@$I1GtsomMsvsS$6byekxby=k{t>i;|NQyw*q14eesw?fHV^cyiF1_RZ z-e0d`wo8s{|EE2KV!HSHrc2(!ad;Y#e!ORXkOQY27|@px=;jH~cK#Tb2OVofHF56J zBlX{HU1~hdffT-ILWmKX$~nR7fy?B+V6cc)z=9swCTS2zG+bQ@UtE85n*OW;rmx8F5AfI){>BmkyywoV%e0RzkwH!2z2iB+~Z{LQ&MN z*BVX8FECgg_Wzv?++cw{J>=_>J{QEFDOCjslsW=%9N0&!#bS1J7r_eY?4DCV2WZ8J*QPX;4T~*FC#BPGbrTVK5(%d8kX&XvCbtYi~4SR3LF~Yfy7(4lD_q$ z+6($4yy2dGP_j##W1#(LpA151=yRX@kkph=w(pKXsp)ble0O@0)Fvl46nRze*@rt&tqwT7JKgN#E)5^KB_|6I3dwaYVBLOh5gmsYk)-rAL9! zVjC3BuUB*MenF30BmpF1t^VgtSO>CZ=3d6P{~1w(^(gqkxJfBVjJ>3vz+K_5FxErk zicMpXaQ_|7n#JeYEPec-_|A;m{A~3$XX_<_L(r>Zq`f%5uo0nBhWFb#XlyDC!4=&& zU6S)SjJ~;w=G5u;F4?xLbCFp3sin5jr!Mcrtvet6CN6s0$LeR(-;29YIN|y!0{EJ$ z_o?IGgWbRg3{yJwLqOHJO-mDXV0a_Hd6980T$B$n>UJ!?NR3@V@g$m`$mm{j&#chLNw2c?gu$)8L7b{Xp#mDry+ZjK3G1BVGEhk?)B68#hZKKB_9BI z9Fk@v(YOlEWopA+7%>heLs)G5p6Ok1j)N}GMp(V70i5|jgWm&;whqDE1##|@cwobP zN!3Er03OG=rw=&*C!8+65HZt#`E*12I;_2kP-K; z!#5idVwf&TAE|WnNbGAW_~~K8>q9rXKQj@?MKVXt`+d@e3H3_E?pf0>IE|`eXA{dV z<4LCh=Kge0O&jBqPs8u5z1;yi&S4=y$2lbN&qZW);2KsxfvBk7h8C_ja73-WR#xNe z)v&>yPm>+9LMG%VF_U5m_8bC33K`;UX47|obr?>W>WT5ZH>H$>Ze7-5_=xePNZ0t( z9mZ-HeSAGlyL3Hb&{}EWci`XYIBmUnXluc@L;~Iq`11vTVY0~8;VEDTH2X&d7UOUx z#uKJl@nFaU4o08U9t(wmuA4&-e|u!j~~en?NO;~`8PU_mVj$Qd@!SnUVo?K7@CX(ec-Y>`7)$-YB%A6%Egu+eBdSW+1}Lqi@5cy2`Lu`w;bi~F}o5y z$rUVFMV8xneHFmF`E)b~ap$CC0gMS{u+0awzzw1fuY9?666fvN#Www7-3^6fI)eZk zFUwEH<>+N-X=<~9iOuj!M^jVtopgEmN=I9}r-#KCZ%oYV{DAeJONuJG8QCMBVBDGX zdbq%l4@p3z7_OH}4BYXZ9Sb?YV0QsPPLCA;$T8vsLeBOu0SG94O|#cZfUMEM{ATad z(fGv3`R~4G{>*iCEnVf<3D{`@*=CE-T=rdPLO-e!#8=1g_WS9Oier-#<2bzR_#{15 zDF2kDHg^Df`c{4w0Rr`kSP7*N7Ie^}xlOjcy({pF$0qC!mUtW7ym;xIs5Ib&0Ui=#FW z-Ge6QsN1nL;|+P0hU;5ZE|!{|IeB+q1J;;>9t*1|2YJapPA=uuKRP+`qi;x%oR5-( zo1a%fvz(@?yt2a9+|ts*+PcFLXU6#mTw1$zZt0C_qTy51SSF&T>`Fq#O8*}7LB+L& zH{W+6644IKo_@QVEVjx(>TG<`VV?|hzh8`ri;sqZnVpQcDhCCDwyey`igEr28E!vH zqSU{@ayYJ$?UMror5v~xO}Y)cZdJP1d^ z8uC<15#DRNJ+}sjjQaNC8W}DsG+ItdUV3bNbYxh@TpEJCCk8HajID z6Bw?_z$QX}M~GH(cJ8=!UF;Lh1Cu2(9e|8U3_eU=(jhxNGO#*3#7GRp)zeTikieqM zva$dq%(^-UF|@@+P0dvOl(hH^6e2tV9ErR3?Mjx9T@qX%PRM}CXtVW5Waj^wm1Fo%R!;L4p*AicDpLBJsE9<^@5Ng{m+&MuJ2R@V zI|lA+usJ(@;KX-rq79hu+~$@xCPqUj?ymQDG$72>T;$|zbadK$sF;|zxL`Yw541v4 zDkr-%4uMZ};~x;NrG3^zknE{JSzry*2D#*L=Xh=PjSWqEP)4;^wh6Oz*91Df;ext+ zgoJX;Nr}7laux6HA6z{Eg1Am5Y<5FoOKApU_b4ifA{Y^VCv=+vV zvb4m@HKMJ(x)PR`k^(rZW6XP(@o?PShe2cZxff6cSb1KieQx(p6`$lihtT=la5KfQ zpmRY(rqi%_ct$dGv;iB)%F62M4k>KGW^jgfh6xK5be`G388CpW@w)R{{TQZi;yd-G zj(>S1cc5u(?!T~dRR04jXa9|r<4LVYzwyNT0eS=V1jX1SG9eZP7bpE|TDq2oe_m7+ zeQ$KoGcz&+{OHi&(h}TvR!&xSW)28}3~#oV4bZ^r7~6GdMDG|$9)vk_ihT-mN@`=T z!Ra?v4mLf&%28(f2PQw}=@KpCkvG$V8AxXmEG}QNlKH z2nc+QsSkL=NvVlQL-fyF&u99e0ek(fph5Uz%cevtkuA&@?C)QsQ2dnOiMbPxc*8f5 zM`6O0yo=>CjSC;QkR|(uxK5b1NnRW;$hj`a58A@0sTm6-Ki!{8W@YUjF4Kz2hftd; z8haP%ysd*QmJb$+RNXEb6 zQ^(jcrgSHt*Jkeqr}_F${e(C4VyX))<%*pG3=Q-zQ8^rdx|_AtIq9#a&Dj4^-Oc*8 z^uMcp{uh-j8|VM=OrG#@6r^9~iq$61l9_T8} zzJw4bSS#vul&=j0aQQ{DU1Wqd*5b6g+l%3ByD`&#Cjt?^~Hf zyv|qDt5cYJE=CuDe8eHe#l=-V{;c3VQ7+|GRg%>ptA6)hiP~uXu@L0mdq1bAyFobfrnqn@vFd-teMN{vMI#=PbOh$WD4A5grshZxj?~rEpxX2)P5|Q9 z(ti`lf@GrtB3T<78v}#4bB>~GyP1cK;{PRP{S z+K5aky#l1@hN7mXrm?ZM7NQh~%MX^_McEsCTV718l;k~d-IRCrt!tkk&1n2?i}6a8*4J6aXIA|gh5C@PAQ28!a@sPB*{lbMmJ^R)%eG(*tL zjH+}FwY;(4!oo%(F;oMB&SZx(kk*Nvx_DTu105xftUhjGkyonF*TKob<@W6C>I!Xv zkDs3pxW;MgXlE2ELwoRsAxgr&N3D*H4|pnTG@~LFBS);r%?X$r8TheEU{2NH<>~3^ z_5Aeo1j)*Ec^#y6nxLzjb#hY4Am3e|q>+$JzSAVETwqV5oPnGERZGKfIu3-tUH;(? zgieSO9wBio0D`@Rk4cdd{_OVl@|^o%NBC=WPJJyCC$s7KDD6G_`xbE2FP6Yvgq}o% z6PeNWYy@maxY%D*L`1Z&w->UZx_Wv#Hg;V__(BMWtD}Q&B~;lED9S896iO0QdT<_) zlTk()m2d=i<)(d-KEf{$+uy<>PJj*y3=Db`$%+s4_rrZ={>RV8xWFDM^p@nDl7n84TtxJjZEC8GFl}v1H78On9``gW(LpSbgVn#-MnQ9h) zI+QF{|M2W$os0>fY_(U;BNgDFqybnp3<1d}49bM@aLDS~x4z^On5ER5MFpfJ4^YDa zeWZCtTj=reDoGXxshXOL+$|ZI=-saB+;5Kw2Nt7q+XaEZlWAzRzMF3qE*iImZz7*P zMBR2)q^e3>Wcdn8UPx0m9W@&%k9=HW`G=|Dx5pMnYC1X^1_so*@Q4UFc=(G;dxu(< z?@1%l;m!M@)5NLSP0%X9-W(#KE3=X%oKsWe$H9&wqVRh??T~CJeF| zC^Yd=RRz?oYVek7YDw?j`6MPKW4zl2q?JX5SxWulK_SD>UVPkA=wvb@`5thvgL7gkk)dGy<5j%pKtueGZIFjGT9lW;j_Bii}hvr)9OjfRi1iDYKgV z`GZrtwOm=J#l1mEsLlO4T*m2MS=4LwKJ75PHomt#1Hq)0lmjhx?SvuPc^(s^!hPsH z89OrzPFxgueo}wW;_6ro3VQr(?c_U*fa=D|jrc=LBbCdBZDjV=uu?*Q#=HTst}h084(?aHiW8ZNNN=soBu=ylGx}DIM)7 zbO9+PAGL4Z4`*4as2=yPY8mLku}X%g+rd(ZQQ?v~Zy}mLb;)~gCbgP8+#eqvprJ-i z;Ecx?f!R7e+o@x%%wsXPn#R_4E}{)wqIcouP7bvxLQ;L`b4TV|Z=D7KXSFw+sxv}GCPeXfq8U6GO z;PD!GG{!yZn)UjY-*68{JO(#qQb3)T8apfnuR_MgD;Z;MT;M2HqO24Y2^xF;;gh_u zg$X^%vW?^Q6(Ukhq{0Wesqx9liB+t*-Cdw&EGniP1fn({-CJ$yL2Dap>xrq35dsl> zV^u%B*C<6?XxLXA$t|-qw6PA!JH*1qt&jqwmX#$fC6+e7j}8g2adEN%ZfJV8&z+sH zoyVP>H!%I@ZtrPqp40TLu0qqPrYMg~koBDi)I)CllBUr^j{c9~J6c>2aLh9KuP*_; zMhV~TQA(Z41hs4(S8?zOGrwkK`c$9opKoq%qA#fUMSAw^Y)Qf+@0t};NrXV3Io4Qm3upSa>XNcX08v(27fQwoMvVZ1mWCEh861$v z3J_SoSKY~Fh8;Zui?3;U{y5crwlH_Z2a}Qo=wr(PdFy9D-a0lml`O<9$ji>bH|_AP zBQ=n2_#n`wbJMiMSQ?$3ZxN&9!M>mdc5CVVLRp#r z`E&lGa0|@v$V_hM$>c}0TIv+|`2-tJk=ou?_^pp4J>whO(Id=gxoLW%#$qhdAI4EO zFGg|iI5;`=;6B^6)YNn~)_#a(MJJPzrZlz?@MlL;A(tKI4@a6ZH?|xxMJmWAW&f5_ z9z3!j$R^;GjiRpi8RYcGx1ZIVz$pl5Wp~CVqOiU}xcRtymSn?fs(g%1DpRB?C@hT| z?3IaB{itM*ufzAeZNMBhn$a76nUY@l>yQ9HHN)%7_2B;SVQ@?x;x@U?1q}NXPF}Ce zy``q}!SmoeU~wd_XHDy8Izx69^|%t{VBp9zpANo~VI_{bZvvYgTfYAlCx3P^ z0?^3vzL%LSJ!@t?a4Y~LZfB5z{9zf-p$J&Sm}zWfj&f` zerPf_of=-9%}ks@`W=h&3yLbic@8`**zk0v{BIiBHcrs`S{`{7X!VaXpasUyy1E{v z2kuz#KO_~T2Y(7Z4WoObjk|^rf!?PDdOn0gTK&8(l5N=OGApcR@P(swa(LShCJD0- z1m)$*BNjH$TX*ApBJUPMr6BY4?XtNa9#3OJKN^$!J9(RWmLS(bza!|ZZsomR(&z3&FKFaW)u${Lv7-{M;usG*Fmy-9?SprT=u=xg;+Yef~r5H?3@h)bPg3 zGQm$(EE>4Fq`BlrSFUAQIw{GC$oj+T?yLO+_(6y?oe=)H1$nbx`QmDl36fygUi>pPX!&bdk>!wQS35;Rd84hg^qKr@ zyexxqGD8HBj|x(RbBU4X$G_#GQCW=@%*K*hM@en`jU$tb^hQE4eVMZ+=qtP+b){O7Pp z9p4xYC;Zsx>}p@n2uO0w5%}9;iPM|4&(`fyUPvDJ>pP>ipDax0imh8J=)YY#Q0Vu6 z972-#BtS?_MjzjVvmAP^%`+96Y5#fr;5W7tkKtIjwbQ<#)9WEB&qtft!;p(O9}MEd zRvCpjaehHd;cU^zZywk7Pgy^NuR#P?s=VR1{Di)f-a~uX6Kth;#5{6}9yyz=>JD`t zRh@9GJYK)L5))r#C9SLN5o1FJ7wqvo=H@qNyM(^ByyG`|cYP^2BPdzqLfHQz;EUP* zGZ*o6&%DRuqw6VZ)u)I@FK)v*|69U_^=CEDHIj$h*hMNjQt#}iSWHhW5Q%f2Up=}f z822tb`g8uL74;i!+}3~~7O&H&(6(|>GAr+y@E+w|rqNYRrE3>0bUsH0at`7iAF;*{ zX#UWvb|lPlQb8BU$nwL`rziZ6PGVk2Xpr9)rT{9%4}XUdK+)>iiT^i6Yc{&86yBuA zc2GZPonuKM*VM7Gh_W?>R83Zw48U<8SKdI~;cg8P2*5aS8>agtEv>4KS!Kxg(;Y89 zx)F2lemnG|098xEmAKm^AM(iNJLUM~&BqQ9&!IkPY0VK85xFnVCjq#Cr1jMs`n|NK zbRr76C8>E##F2{zKx$Twdf)qN)_~~eIdH3v&mw=n2>L{a=KUUKaJ5h2QHNYYnt5zQ zItQbLX)^B2C)J@9ev}k@70)F_@Fk_P5NfAVK01(NlJzxZ^-a_22I;JMv&U)+_G0vi z)Y;Ct+U4|LtIU3GK+`%0F8K17#ra;mpuhef4m{3WHq7Ti z5HEJ%v3`Axh&f?_3U8rsu62{LOB*${A{oQXyuXBo#_$DG1c4I*r3Qh_0r~&@Bjx=W zDs+0};1F~gLCKfY^mK%GYeNS@aBSSScPW+>FNYgM`6F%|DyGRXmvXwydck z=UlW11uh)6Y^d3p7L-9R*HQPXo}*KrsM~t(UE!LEem=EdUg{YQ_;?nGnLC$Av{GGU zXTs2V=~Pq8MVtmf^4I*PtBlnah)}_AZ5W;r86aPKa4zcq=@LG za3FqjNC>z4tD|kQVp*^IZk0&+4Q@Ck}Wr;_T zc?NlaH$Rh6A3lQ&T42Bvh@6@b|4bO}2J;vF=1UBmo}dK;y3Oz}EU{o>-&&M5xP&DJ zAb&xF!}{fZgsXnVh$1%1Qswa4)Q@H7XpJ2B`BR-6gpF6VFQh=1+$D>QtVJ;ZlVSKe z42*OCEUO`?=nz=<>vz#zDYC6~W0s9xe#XpU$j8Oo+a{OsQ*-A6s9(8!_}t?`+3mr6 z!cep1pdrO^-Ttj4Q?zj3)9Y)aOiSgdEStx^M^FuG?D}imy}ac1F{G|F2NnIEDaJh_ zZ*c=hM8dUSuFFopkb6`f3DS6skKHEbg3l8rk*HUrW>fPMm^*u~$J%XVJg2=+K0xx` zlU~ViiN<>=1Os!#Fws_!<}yF$3-uH+)96$0aZPnmt>w|Nxq&H(*Mn+6$WF8|OY}C* zB;>o#6sOzc{??=K;BjU`!q)loM&Parp%6p8C@WDJzrzZHt^vlB}F4wS6hyPy69=1ok>$-o}VU2Cb-+ z7djotdsV61?lC~65rmOV%KSRW65Yk_WL=B(s`N!|OzL&)%sNLW9xyY%$qD8XdwzGK z!a{0z1TBg$CxOVk+%TidmD>>FS719Rgv_RcJ)aNGZVq$8x~yH@?kh;2Od*Gwzk=k# z6xh7)-^z-%S&=SS6NY*|k{%OIQ8JOe2fK>w~*tX=$|&vdvNqwn}$xfnupQ(TvE(*2{p3{;C!Pwchj+Pa)R_9`6i;>__} zB=&lq8H%Y7Cdl)eI?LaioRU^v)$r{ zS+5rUlsVS0EuD^8(0RYZIY03V<{+0H?q~E8tsO?M~-5UQFXo;MA}Z$u%N@D zB>Z=iSye)Z^6W%Rtf*Kc(8&OB{yU+ zo^U@ljNeXfrC+X!w9z~35vi7%=u%_Nr!2t=FB)W5NfSr2Xy*5AX5oH_m07SFIO+aM z^Q(Kb6BGS22VmH=G{|>fAcM=<^ILIQrDvhkr%g<5M?TlX{`6hyxQfN<9zI$J!OrrY zw*!srVG5UgZ;va*L^@y|gAHQD1Tr7oh9EHxmdptj$^NW+bNQ|3jK1<&;jz8E-$+-( zS6Jb`PURX0&n-Fc4t<~~R4I8CnU9F8$eWS(5I$iFEm9g0@evz*Q^n7(_R&|M-DDU} z;>n~gdEBU?^tx)I&=j|35vCztE8|)!pEW!0=tb)O@z@G6Qc6$@PT!#aCpmT7?eFu! zxF7{1IT3mKreBQIPhfAqeGho9 z1_M6a^W?H@_YNU()fU}VwGS3*FC61-V?UC9MtdLvi>rRPaasNJ2RgwHq#V%|F>-_w zV)Vr7qT^(b9hPC-v=ZhMBdEv3GBa0%o-;zko)TSLPk9k#1OdQ&~~~ON#bGEM&7eN@#2|c(b=0pNW404n?riOIwKLUfH1iYvHIl{GN#|32E4B9aM@a~ zgc6MvS$vj=0gRQtKzjl(hzio-6BH6xq0ilBnP`Dk+bSO;b`}7ueu1i7a|wfmDvqLV zJ5@Oi>gs@k8a-h=x=NO9!vr>{#YVIV>})?K*64}o-T8&^{M+uf^c&&6bv@Cn2WNEa zTTAg82uH8Luzx15o)cXl4fK4jhQ#hHkYX}VXrs7&s#CSQkB2<%Qg|%JI1^-_DmZ-% z07IaUv^R0AQb_5hj;x>g(Ijun+4`-AdOw_>#h01*SRxBpGE_-Cx7aIX3Hif?JV*VZW- ziv`{eZ83whh~R}iHF|FJGwW8;56?_1SsyqoRHi%KE|5qT#H{>Hc(Wk5YDA;&WhKs<^{GliI!$-^tlUuG+ zRXa*AN8b&Hu*xq&62-)-eQ9>NDq(8}e08p!3rq=I*gM*WsC}5_ zHnA?_=J`6v1W`>0xNO<O8Apx z#cTZP4HE2A^UEa>jh&6g2EhlXCO$zMI*9NkX>uJWmwPY4oC5RNPs2d#0Zk4AO}8fx z=hjrga!$jwdp!vvAa}$-rN5fKQo4UV6M^R|q8mHmXj81XpjaLB7r?;6aZTW%bX&MD zxz`iRg1cOwKg>QlHsqE9H@tdOnSV=Np^F^MpE*;AfUR06G%+C3Z~iIKE}B^u%O(u_5B@!(1)wNUwXWnAK4h0DficJo*#VV^Zc(tTg z#Len5N=`5$wSZZFTr@am!xII2B}|W<3>)z=`W0b&YP+)hCiMUtdg;pf z*u6n`O&SER6N|rXC<3+kiG<93K>8G}8u}WC#)D_Cn~&5%?gJ%T`WYyjyUVlfmMmTL zXZ;TeG2~=3)txQrBXca)Gw$XrQf#u^ge4M=;wSNKTG`{dKl{*OSw*_=gA)-!N%dqt z#ACd2dym5LX(9gh2_N8Qu+Nu0KfCW|e0RD<-Q78sp3%aUX0Tu03kB!luXC>aUFxm; zYZk~X+30(};+hq~V~XKpwEeu{1p{eGH#q=5##&Y#=M_l)fSOh&q;6Tzv>oXs9Wn_` zrT1gbSf zf?rr!{|D1%|BnNU*#9!?mi2AA@V`AV$i)3`x?tx2D_OUC7>nNr|94rpN_qY;p?P1K zb1T2KJ+{0qJw=v-GY@cFKSHRkZg*`R}~Ut*k6i`C|hZTy69sqIidWBgrMeW=XYqcv@SFHTT2#o2~Z`5BK%; zgrIGxx)H-<>>p`9N8*S|^mFM4%ASs=*1>oM`h5-H5W?g3kWfHg{tn4K7?pq*avV$H zmx7}@NHz<~#J>(kBl;G?L1tuRgw6~-Zwutzc3|Uztl{`>wH%im#|9-Pq~hYlqxFLz zv`6}m&drUFYZG9SB3v&`Oni_#kKlyjjRohj3s}MC7@ZMSTAn}%E0x8a+&w=&}lW4SzT_tiqwM937x5X2Nz14tTi zzPUl7!ymw1xi~&PKErDQ<$-r#-Gpk%oacA4l>7``Eb%NDPASh?p%RM|GCscYeROFa zvmQUwn8nNt3rqb=NJK@xE5 zW{GKLlej?OZ7L$_-ve(){~36D_@4rAZGgbrbRh6H9di~}Eg*o6uqeRsJa9osgOfT=WDOzxD;k~+LcIJah73~`z&6O|R&w##>YI<#cru77*3K63mhBV(oA zlVLK9sX1wHXJ^;g%%rHekes4_Kr9G6Jtk#kU}P;gzv!F1Vo&21^hfS5IW@RK;~A^` zUKql;s54)}Ml4b_x!vv|NAor}ea)}0FKBKqQPhmhPESb9O3OfzU3hBWsbtB* zC9rB{LrKOp*<%DO0JFRixFDts_KaQKv1j|u1#^){E1o7wVP)m*wPj_kMMWRylvH$d z)HIbUY3Lc~hNnh8kbO1~h9g4GGc^dub^jEChS4CDgHioGY%~mfVtOIcQDR(#%m`hX zrS=j&TxN`>%+m7rAs%je8i1`$*Am0ka907cDZQTWmkvkm)oS7mB4Ut`Z=l(!JggiE zAKbP5;bagO@5dNXa0$$dk~xrAT$tb^!U2BBC@uBJB=sNj>}>2!y3Gso&ZQk4qU|$l z-UrHB%LDVtMT72kw6yOb8WRJ*hN4JJOpdPfE7G(3RR+Kwb5XIOJ6L(7exqWJP?#L% zR#$~HzLu-N0&+rl+A1AgZcFmjRdaQn6mF9$E5`^@mOiE9{kEhT3r}*>v+{;Zj$)`$E*wFG zi@$&R`{1P2^S}_kTCD``vQC(uPh(?;p{}XXckarrOzHi?tz!O>D*S|h{Hzu#8VhKIT}^Fu&xW$1)=6cdB?E!b5ALlz zi=WPwMc*$73i@=vn+r=Qb|hhQpyj0^H&b+_vscmlq@?vrWEeu(o(sS7>oG)rEtIc~Vp9?HeBrmB8K$MPxD-rUxZ$MuRa|Rc2NPdI} zl%Oiu!AMk{H#nu73|6|pEDrn^{_qimY{1z8@aTgsHd)_V(s=t*Ih(46s!>BKI}m{WXU@d z_hgZ~#$nni%VDKu~%5Ey$K-OPU<#f{~6dw9B!n3zb}jGma7z?uS5VcToG zZLg6mjt}(}pbI{_GqAp=qX0y(vhE~j+ka&nP3tflXE3;Khgm7n`fczNe;XtS4{KoCPR5#&B$ z*Q#g^1_py$dw)s&%I|ZI)(zzDu7&XdKRVqm4|gt$V~E!DbXwURygr8(eALWHNk+km zd>BDuFN1*gEh@Gh$3?=o_)^L(zT@=4{HGb_$qI#Odexfm&&OLLl&)}g^K+uLE}y3B zo9i(w1tLKWtrmVkr>KH?jS8NWIH{1d^asWKix#td37P!OFdqGXNxZFjOT4vwxf$&U zQOL6H^u9ddpLrM_uq8$CK3M2~bg}&R$!p!OFYzBs?c|2 za%xd5OmQ_jtWt=@OXL@qhF)r4Y;YOiJT)np=KmLW_ZVDB`2Gz(HYT=h+qUhAZ6_z1 zU}D?G#I`-riEZ1MaP#{=&pwO#zu0}TZ?>wdtL{^MPIXuHsp|W>?*4qCo6z1w#TLP&PLohOVA#LD%dk z(@B(t$p$_x;Vwh?=ohstyQ6m!=f?)qEiyo(^%K4{xTe#xW~e(P?Ji>=IZE?(!y_2pel_ zgJJsprE{@8zxVAcLylEI&=V2Mu!B{=ueGwczP96c4jrCcd zo-X2~npGaFFv}Cc10~9#AWPws@5oqH4I4;5`mj(eH6tLEX*4)Pa5p!igD5v2&< z1ry5Rj^l5S6^_sI&o9)b+J7nUo3d~BGqpO0y%)CRRz~;m|FUM9iip{1^WAVl*YT^` z4_}+Qbz{4)Jm-54`kJX2i|p{*j-Gno*gM$7m(Pvvqdu1X5Jlbq#8$om(tV#QV1h

FnJ6k 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õ|we|6J%X{m(@m(^ytchawMN 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 tutorial slides from the 2013 Trilinos Users' Group meeting. + +\section Kokkos_Classic %Kokkos Classic + +"%Kokkos Classic" consists of computational kernels that support the +%Tpetra package. These kernels include sparse matrix-vector multiply, +sparse triangular solve, Gauss-Seidel, and dense vector operations. +They are templated on the type of objects (\c Scalar) on which they +operate. This component was not meant to be visible to users; it is +an implementation detail of the %Tpetra distributed linear algebra +package. + +%Kokkos Classic also implements a shared-memory parallel programming +model. This inspired and preceded the %Kokkos programming model +described in the previous section. Users should consider the %Kokkos +Classic programming model deprecated, and prefer the new %Kokkos +programming model. +*/ diff --git a/lib/kokkos/example/CMakeLists.txt b/lib/kokkos/example/CMakeLists.txt new file mode 100644 index 0000000000..3809cc2ea5 --- /dev/null +++ b/lib/kokkos/example/CMakeLists.txt @@ -0,0 +1,20 @@ + + +# Subpackage name must match what appears in kokkos/cmake/Dependencies.cmake +# +TRIBITS_SUBPACKAGE(Example) + +TRIBITS_ADD_EXAMPLE_DIRECTORIES(query_device) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(fixture) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(feint) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(fenl) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(multi_fem) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(md_skeleton) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(global_2_local_ids) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(grow_array) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(sort_array) +if(NOT Kokkos_ENABLE_Cuda) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(tutorial) +endif() +TRIBITS_SUBPACKAGE_POSTPROCESS() + diff --git a/lib/kokkos/example/cmake/Dependencies.cmake b/lib/kokkos/example/cmake/Dependencies.cmake new file mode 100644 index 0000000000..0d86e78712 --- /dev/null +++ b/lib/kokkos/example/cmake/Dependencies.cmake @@ -0,0 +1,4 @@ +TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( + LIB_REQUIRED_DEP_PACKAGES KokkosCore KokkosContainers KokkosAlgorithms + TEST_OPTIONAL_DEP_TPLS CUSPARSE MKL + ) diff --git a/lib/kokkos/example/feint/CMakeLists.txt b/lib/kokkos/example/feint/CMakeLists.txt new file mode 100644 index 0000000000..0018b9f9f5 --- /dev/null +++ b/lib/kokkos/example/feint/CMakeLists.txt @@ -0,0 +1,18 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../common) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../fixture) + +SET(SOURCES "") + +FILE(GLOB SOURCES *.cpp) + +LIST( APPEND SOURCES ../fixture/BoxElemPart.cpp) + +TRIBITS_ADD_EXECUTABLE( + feint + SOURCES ${SOURCES} + COMM serial mpi + ) + diff --git a/lib/kokkos/example/fenl/CMakeLists.txt b/lib/kokkos/example/fenl/CMakeLists.txt new file mode 100644 index 0000000000..150656b16e --- /dev/null +++ b/lib/kokkos/example/fenl/CMakeLists.txt @@ -0,0 +1,17 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../common) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../fixture) + +SET(SOURCES "") + +FILE( GLOB SOURCES *.cpp ) + +LIST( APPEND SOURCES ../fixture/BoxElemPart.cpp ) + +TRIBITS_ADD_EXECUTABLE( + fenl + SOURCES ${SOURCES} + COMM serial mpi + ) diff --git a/lib/kokkos/example/fenl/Makefile b/lib/kokkos/example/fenl/Makefile index 491ed4ee6d..2e64e11e1e 100644 --- a/lib/kokkos/example/fenl/Makefile +++ b/lib/kokkos/example/fenl/Makefile @@ -1,15 +1,20 @@ -KOKKOS_PATH = ../.. +KOKKOS_PATH ?= ../.. -vpath %.cpp ${KOKKOS_PATH}/example/fixture ${KOKKOS_PATH}/example/fenl +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) -EXAMPLE_HEADERS = $(wildcard $(KOKKOS_PATH)/example/common/*.hpp ${KOKKOS_PATH}/example/fixture/*.hpp ${KOKKOS_PATH}/example/fenl/*.hpp) +vpath %.cpp ${SRC_DIR}/../fixture ${SRC_DIR} + +EXAMPLE_HEADERS = $(wildcard $(SRC_DIR)/../common/*.hpp ${SRC_DIR}/../fixture/*.hpp ${SRC_DIR}/*.hpp) default: build_all echo "End Build" - + include $(KOKKOS_PATH)/Makefile.kokkos -ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) +# KOKKOS_INTERNAL_USE_CUDA is not exported to installed Makefile.kokkos +# use KOKKOS_DEVICE here +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) CXX = nvcc_wrapper CXXFLAGS ?= -O3 LINK = $(CXX) @@ -22,9 +27,9 @@ else endif KOKKOS_CXXFLAGS += \ - -I${KOKKOS_PATH}/example/common \ - -I${KOKKOS_PATH}/example/fixture \ - -I${KOKKOS_PATH}/example/fenl + -I${SRC_DIR}/../common \ + -I${SRC_DIR}/../fixture \ + -I${SRC_DIR} EXE_EXAMPLE_FENL = KokkosExample_Fenl @@ -42,6 +47,8 @@ build_all : $(TARGETS) test : build_all +clean: + rm -f *.o $(EXE_EXAMPLE_FENL) KokkosCore_config.* # Compilation rules diff --git a/lib/kokkos/example/fixture/CMakeLists.txt b/lib/kokkos/example/fixture/CMakeLists.txt new file mode 100644 index 0000000000..298c54c5bb --- /dev/null +++ b/lib/kokkos/example/fixture/CMakeLists.txt @@ -0,0 +1,13 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../common) + +SET(SOURCES_TEST Main.cpp TestFixture.cpp BoxElemPart.cpp ) + +# Automatically picks up 'kokkosexample_fixture' +TRIBITS_ADD_EXECUTABLE_AND_TEST( + TestFixture + SOURCES ${SOURCES_TEST} + ) + diff --git a/lib/kokkos/example/global_2_local_ids/CMakeLists.txt b/lib/kokkos/example/global_2_local_ids/CMakeLists.txt new file mode 100644 index 0000000000..9f32fe5802 --- /dev/null +++ b/lib/kokkos/example/global_2_local_ids/CMakeLists.txt @@ -0,0 +1,17 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES "") + +SET(SOURCES + G2L_Main.cpp + ) + +TRIBITS_ADD_EXECUTABLE( + global_2_local_ids + SOURCES ${SOURCES} + COMM serial mpi + ) + + diff --git a/lib/kokkos/example/global_2_local_ids/Makefile b/lib/kokkos/example/global_2_local_ids/Makefile new file mode 100644 index 0000000000..bf8fbea3e0 --- /dev/null +++ b/lib/kokkos/example/global_2_local_ids/Makefile @@ -0,0 +1,53 @@ +KOKKOS_PATH ?= ../.. + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) + +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:$(SRC_DIR)/%.cpp=%.o) + +#SRC = $(wildcard *.cpp) +#OBJ = $(SRC:%.cpp=%.o) + +default: build + echo "Start Build" + +# use installed Makefile.kokkos +include $(KOKKOS_PATH)/Makefile.kokkos + +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) +CXX = $(NVCC_WRAPPER) +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .cuda, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "Cuda,OpenMP" +#KOKKOS_ARCH = "SNB,Kepler35" +else +CXX = g++ +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .host, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "OpenMP" +#KOKKOS_ARCH = "SNB" +endif + +DEPFLAGS = -M + +LIB = + + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: + rm -f *.a *.o *.cuda *.host + +# Compilation rules + +%.o:$(SRC_DIR)/%.cpp $(KOKKOS_CPP_DEPENDS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< + diff --git a/lib/kokkos/example/grow_array/CMakeLists.txt b/lib/kokkos/example/grow_array/CMakeLists.txt new file mode 100644 index 0000000000..d9ff170492 --- /dev/null +++ b/lib/kokkos/example/grow_array/CMakeLists.txt @@ -0,0 +1,14 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES "") + +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_EXECUTABLE( + grow_array + SOURCES ${SOURCES} + COMM serial mpi + ) + diff --git a/lib/kokkos/example/grow_array/Makefile b/lib/kokkos/example/grow_array/Makefile new file mode 100644 index 0000000000..bf8fbea3e0 --- /dev/null +++ b/lib/kokkos/example/grow_array/Makefile @@ -0,0 +1,53 @@ +KOKKOS_PATH ?= ../.. + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) + +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:$(SRC_DIR)/%.cpp=%.o) + +#SRC = $(wildcard *.cpp) +#OBJ = $(SRC:%.cpp=%.o) + +default: build + echo "Start Build" + +# use installed Makefile.kokkos +include $(KOKKOS_PATH)/Makefile.kokkos + +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) +CXX = $(NVCC_WRAPPER) +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .cuda, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "Cuda,OpenMP" +#KOKKOS_ARCH = "SNB,Kepler35" +else +CXX = g++ +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .host, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "OpenMP" +#KOKKOS_ARCH = "SNB" +endif + +DEPFLAGS = -M + +LIB = + + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: + rm -f *.a *.o *.cuda *.host + +# Compilation rules + +%.o:$(SRC_DIR)/%.cpp $(KOKKOS_CPP_DEPENDS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< + diff --git a/lib/kokkos/example/md_skeleton/CMakeLists.txt b/lib/kokkos/example/md_skeleton/CMakeLists.txt new file mode 100644 index 0000000000..28412c3784 --- /dev/null +++ b/lib/kokkos/example/md_skeleton/CMakeLists.txt @@ -0,0 +1,16 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES "") +SET(LIBRARIES "") + +FILE(GLOB SOURCES *.cpp ) + +TRIBITS_ADD_EXECUTABLE( + md_skeleton + SOURCES ${SOURCES} + COMM serial mpi + DEPLIBS ${LIBRARIES} + ) + diff --git a/lib/kokkos/example/md_skeleton/Makefile b/lib/kokkos/example/md_skeleton/Makefile new file mode 100644 index 0000000000..bf8fbea3e0 --- /dev/null +++ b/lib/kokkos/example/md_skeleton/Makefile @@ -0,0 +1,53 @@ +KOKKOS_PATH ?= ../.. + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) + +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:$(SRC_DIR)/%.cpp=%.o) + +#SRC = $(wildcard *.cpp) +#OBJ = $(SRC:%.cpp=%.o) + +default: build + echo "Start Build" + +# use installed Makefile.kokkos +include $(KOKKOS_PATH)/Makefile.kokkos + +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) +CXX = $(NVCC_WRAPPER) +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .cuda, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "Cuda,OpenMP" +#KOKKOS_ARCH = "SNB,Kepler35" +else +CXX = g++ +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .host, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "OpenMP" +#KOKKOS_ARCH = "SNB" +endif + +DEPFLAGS = -M + +LIB = + + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: + rm -f *.a *.o *.cuda *.host + +# Compilation rules + +%.o:$(SRC_DIR)/%.cpp $(KOKKOS_CPP_DEPENDS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< + diff --git a/lib/kokkos/example/multi_fem/CMakeLists.txt b/lib/kokkos/example/multi_fem/CMakeLists.txt new file mode 100644 index 0000000000..e3a40bc26f --- /dev/null +++ b/lib/kokkos/example/multi_fem/CMakeLists.txt @@ -0,0 +1,16 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES "") + +FILE(GLOB SOURCES *.cpp) + +SET(LIBRARIES kokkoscore) + +TRIBITS_ADD_EXECUTABLE( + multi_fem + SOURCES ${SOURCES} + COMM serial mpi + ) + diff --git a/lib/kokkos/example/multi_fem/Makefile b/lib/kokkos/example/multi_fem/Makefile new file mode 100644 index 0000000000..72e1768fcb --- /dev/null +++ b/lib/kokkos/example/multi_fem/Makefile @@ -0,0 +1,53 @@ +KOKKOS_PATH ?= ../.. + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) + +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:$(SRC_DIR)/%.cpp=%.o) + +#SRC = $(wildcard *.cpp) +#OBJ = $(SRC:%.cpp=%.o) + +default: build + echo "Start Build" + +# use installed Makefile.kokkos +include $(KOKKOS_PATH)/Makefile.kokkos + +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) +CXX = $(NVCC_WRAPPER) +CXXFLAGS = -I$(SRC_DIR) -I$(CUDA_PATH) -O3 +LINK = $(CXX) +LINKFLAGS = -L$(CUDA_PATH)/lib64 -lcusparse +EXE = $(addsuffix .cuda, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "Cuda,OpenMP" +#KOKKOS_ARCH = "SNB,Kepler35" +else +CXX = g++ +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .host, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "OpenMP" +#KOKKOS_ARCH = "SNB" +endif + +DEPFLAGS = -M + +LIB = + + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: + rm -f *.a *.o *.cuda *.host + +# Compilation rules + +%.o:$(SRC_DIR)/%.cpp $(KOKKOS_CPP_DEPENDS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< + diff --git a/lib/kokkos/example/query_device/CMakeLists.txt b/lib/kokkos/example/query_device/CMakeLists.txt new file mode 100644 index 0000000000..dade7f01fe --- /dev/null +++ b/lib/kokkos/example/query_device/CMakeLists.txt @@ -0,0 +1,14 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES "") + +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_EXECUTABLE( + query_device + SOURCES ${SOURCES} + COMM serial mpi + ) + diff --git a/lib/kokkos/example/query_device/Makefile b/lib/kokkos/example/query_device/Makefile new file mode 100644 index 0000000000..bf8fbea3e0 --- /dev/null +++ b/lib/kokkos/example/query_device/Makefile @@ -0,0 +1,53 @@ +KOKKOS_PATH ?= ../.. + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) + +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:$(SRC_DIR)/%.cpp=%.o) + +#SRC = $(wildcard *.cpp) +#OBJ = $(SRC:%.cpp=%.o) + +default: build + echo "Start Build" + +# use installed Makefile.kokkos +include $(KOKKOS_PATH)/Makefile.kokkos + +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) +CXX = $(NVCC_WRAPPER) +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .cuda, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "Cuda,OpenMP" +#KOKKOS_ARCH = "SNB,Kepler35" +else +CXX = g++ +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .host, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "OpenMP" +#KOKKOS_ARCH = "SNB" +endif + +DEPFLAGS = -M + +LIB = + + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: + rm -f *.a *.o *.cuda *.host + +# Compilation rules + +%.o:$(SRC_DIR)/%.cpp $(KOKKOS_CPP_DEPENDS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< + diff --git a/lib/kokkos/example/sort_array/CMakeLists.txt b/lib/kokkos/example/sort_array/CMakeLists.txt new file mode 100644 index 0000000000..3e58198d7b --- /dev/null +++ b/lib/kokkos/example/sort_array/CMakeLists.txt @@ -0,0 +1,15 @@ +INCLUDE(TribitsAddExecutableAndTest) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SOURCES "") + +FILE(GLOB SOURCES *.cpp) + +TRIBITS_ADD_EXECUTABLE( + sort_array + SOURCES ${SOURCES} + COMM serial mpi + ) + diff --git a/lib/kokkos/example/sort_array/Makefile b/lib/kokkos/example/sort_array/Makefile new file mode 100644 index 0000000000..bf8fbea3e0 --- /dev/null +++ b/lib/kokkos/example/sort_array/Makefile @@ -0,0 +1,53 @@ +KOKKOS_PATH ?= ../.. + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +SRC_DIR := $(dir $(MAKEFILE_PATH)) + +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:$(SRC_DIR)/%.cpp=%.o) + +#SRC = $(wildcard *.cpp) +#OBJ = $(SRC:%.cpp=%.o) + +default: build + echo "Start Build" + +# use installed Makefile.kokkos +include $(KOKKOS_PATH)/Makefile.kokkos + +ifneq (,$(findstring Cuda,$(KOKKOS_DEVICES))) +CXX = $(NVCC_WRAPPER) +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .cuda, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "Cuda,OpenMP" +#KOKKOS_ARCH = "SNB,Kepler35" +else +CXX = g++ +CXXFLAGS = -I$(SRC_DIR) -O3 +LINK = $(CXX) +LINKFLAGS = +EXE = $(addsuffix .host, $(shell basename $(SRC_DIR))) +#KOKKOS_DEVICES = "OpenMP" +#KOKKOS_ARCH = "SNB" +endif + +DEPFLAGS = -M + +LIB = + + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: + rm -f *.a *.o *.cuda *.host + +# Compilation rules + +%.o:$(SRC_DIR)/%.cpp $(KOKKOS_CPP_DEPENDS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< + diff --git a/lib/kokkos/example/tutorial/01_hello_world/CMakeLists.txt b/lib/kokkos/example/tutorial/01_hello_world/CMakeLists.txt new file mode 100644 index 0000000000..5e5b1fcb46 --- /dev/null +++ b/lib/kokkos/example/tutorial/01_hello_world/CMakeLists.txt @@ -0,0 +1,11 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_01_hello_world + SOURCES hello_world.cpp + COMM serial mpi + ) + diff --git a/lib/kokkos/example/tutorial/01_hello_world_lambda/CMakeLists.txt b/lib/kokkos/example/tutorial/01_hello_world_lambda/CMakeLists.txt new file mode 100644 index 0000000000..3fcca4bceb --- /dev/null +++ b/lib/kokkos/example/tutorial/01_hello_world_lambda/CMakeLists.txt @@ -0,0 +1,13 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +IF (Kokkos_ENABLE_CXX11) + # This is a tutorial, not a test, so we don't ask CTest to run it. + TRIBITS_ADD_EXECUTABLE( + tutorial_01_hello_world_lambda + SOURCES hello_world_lambda.cpp + COMM serial mpi + ) +ENDIF () + diff --git a/lib/kokkos/example/tutorial/02_simple_reduce/CMakeLists.txt b/lib/kokkos/example/tutorial/02_simple_reduce/CMakeLists.txt new file mode 100644 index 0000000000..7c78db840f --- /dev/null +++ b/lib/kokkos/example/tutorial/02_simple_reduce/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_02_simple_reduce + SOURCES simple_reduce.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/02_simple_reduce_lambda/CMakeLists.txt b/lib/kokkos/example/tutorial/02_simple_reduce_lambda/CMakeLists.txt new file mode 100644 index 0000000000..e2e3a929f1 --- /dev/null +++ b/lib/kokkos/example/tutorial/02_simple_reduce_lambda/CMakeLists.txt @@ -0,0 +1,12 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +IF (Kokkos_ENABLE_CXX11) + # This is a tutorial, not a test, so we don't ask CTest to run it. + TRIBITS_ADD_EXECUTABLE( + tutorial_02_simple_reduce_lambda + SOURCES simple_reduce_lambda.cpp + COMM serial mpi + ) +ENDIF () diff --git a/lib/kokkos/example/tutorial/03_simple_view/CMakeLists.txt b/lib/kokkos/example/tutorial/03_simple_view/CMakeLists.txt new file mode 100644 index 0000000000..7475a99e49 --- /dev/null +++ b/lib/kokkos/example/tutorial/03_simple_view/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_03_simple_view + SOURCES simple_view.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/03_simple_view_lambda/CMakeLists.txt b/lib/kokkos/example/tutorial/03_simple_view_lambda/CMakeLists.txt new file mode 100644 index 0000000000..601fe452a4 --- /dev/null +++ b/lib/kokkos/example/tutorial/03_simple_view_lambda/CMakeLists.txt @@ -0,0 +1,12 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +IF (Kokkos_ENABLE_CXX11) + # This is a tutorial, not a test, so we don't ask CTest to run it. + TRIBITS_ADD_EXECUTABLE( + tutorial_03_simple_view_lambda + SOURCES simple_view_lambda.cpp + COMM serial mpi + ) +ENDIF () diff --git a/lib/kokkos/example/tutorial/04_simple_memoryspaces/CMakeLists.txt b/lib/kokkos/example/tutorial/04_simple_memoryspaces/CMakeLists.txt new file mode 100644 index 0000000000..09f209077a --- /dev/null +++ b/lib/kokkos/example/tutorial/04_simple_memoryspaces/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_04_simple_memoryspaces + SOURCES simple_memoryspaces.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/05_simple_atomics/CMakeLists.txt b/lib/kokkos/example/tutorial/05_simple_atomics/CMakeLists.txt new file mode 100644 index 0000000000..5a5790fb04 --- /dev/null +++ b/lib/kokkos/example/tutorial/05_simple_atomics/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_05_simple_atomics + SOURCES simple_atomics.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Advanced_Views/01_data_layouts/CMakeLists.txt b/lib/kokkos/example/tutorial/Advanced_Views/01_data_layouts/CMakeLists.txt new file mode 100644 index 0000000000..2eb3a8f6c9 --- /dev/null +++ b/lib/kokkos/example/tutorial/Advanced_Views/01_data_layouts/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_advancedviews_01_data_layouts + SOURCES data_layouts.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Advanced_Views/02_memory_traits/CMakeLists.txt b/lib/kokkos/example/tutorial/Advanced_Views/02_memory_traits/CMakeLists.txt new file mode 100644 index 0000000000..1963e544d7 --- /dev/null +++ b/lib/kokkos/example/tutorial/Advanced_Views/02_memory_traits/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_advancedviews_02_memory_traits + SOURCES memory_traits.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Advanced_Views/03_subviews/CMakeLists.txt b/lib/kokkos/example/tutorial/Advanced_Views/03_subviews/CMakeLists.txt new file mode 100644 index 0000000000..cbe394c78b --- /dev/null +++ b/lib/kokkos/example/tutorial/Advanced_Views/03_subviews/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_advancedviews_03_subviews + SOURCES subviews.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Advanced_Views/04_dualviews/CMakeLists.txt b/lib/kokkos/example/tutorial/Advanced_Views/04_dualviews/CMakeLists.txt new file mode 100644 index 0000000000..300dab128e --- /dev/null +++ b/lib/kokkos/example/tutorial/Advanced_Views/04_dualviews/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_advancedviews_04_dualviews + SOURCES dual_view.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/CMakeLists.txt b/lib/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/CMakeLists.txt new file mode 100644 index 0000000000..f0ed569f9f --- /dev/null +++ b/lib/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/CMakeLists.txt @@ -0,0 +1,13 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +IF (Kokkos_ENABLE_Cuda_UVM) + # This is a tutorial, not a test, so we don't ask CTest to run it. + TRIBITS_ADD_EXECUTABLE( + tutorial_advancedviews_05_nvidia_uvm + SOURCES uvm_example.cpp + COMM serial mpi + DEPLIBS kokkoscontainers kokkoscore + ) +ENDIF () diff --git a/lib/kokkos/example/tutorial/Advanced_Views/CMakeLists.txt b/lib/kokkos/example/tutorial/Advanced_Views/CMakeLists.txt new file mode 100644 index 0000000000..f4f1addc55 --- /dev/null +++ b/lib/kokkos/example/tutorial/Advanced_Views/CMakeLists.txt @@ -0,0 +1,9 @@ + +TRIBITS_ADD_EXAMPLE_DIRECTORIES(01_data_layouts) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(02_memory_traits) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(03_subviews) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(04_dualviews) + +IF (Kokkos_ENABLE_Cuda_UVM) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(05_NVIDIA_UVM) +ENDIF () diff --git a/lib/kokkos/example/tutorial/CMakeLists.txt b/lib/kokkos/example/tutorial/CMakeLists.txt new file mode 100644 index 0000000000..d1fd4c0ae9 --- /dev/null +++ b/lib/kokkos/example/tutorial/CMakeLists.txt @@ -0,0 +1,17 @@ + +TRIBITS_ADD_EXAMPLE_DIRECTORIES(01_hello_world) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(02_simple_reduce) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(03_simple_view) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(04_simple_memoryspaces) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(05_simple_atomics) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(Advanced_Views) +TRIBITS_ADD_EXAMPLE_DIRECTORIES(Hierarchical_Parallelism) + +IF (Kokkos_ENABLE_CXX11) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(01_hello_world_lambda) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(02_simple_reduce_lambda) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(03_simple_view_lambda) +ENDIF () + + + diff --git a/lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams/CMakeLists.txt b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams/CMakeLists.txt new file mode 100644 index 0000000000..2d8a514a45 --- /dev/null +++ b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_hierarchicalparallelism_01_thread_teams + SOURCES thread_teams.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams_lambda/CMakeLists.txt b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams_lambda/CMakeLists.txt new file mode 100644 index 0000000000..ec7f1e1159 --- /dev/null +++ b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/01_thread_teams_lambda/CMakeLists.txt @@ -0,0 +1,13 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +IF (Kokkos_ENABLE_CXX11) + # This is a tutorial, not a test, so we don't ask CTest to run it. + TRIBITS_ADD_EXECUTABLE( + tutorial_hierarchical_01_thread_teams_lambda + SOURCES thread_teams_lambda.cpp + COMM serial mpi + ) +ENDIF () + diff --git a/lib/kokkos/example/tutorial/Hierarchical_Parallelism/02_nested_parallel_for/CMakeLists.txt b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/02_nested_parallel_for/CMakeLists.txt new file mode 100644 index 0000000000..e660405345 --- /dev/null +++ b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/02_nested_parallel_for/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_hierarchicalparallelism_02_nested_parallel_for + SOURCES nested_parallel_for.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Hierarchical_Parallelism/03_vectorization/CMakeLists.txt b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/03_vectorization/CMakeLists.txt new file mode 100644 index 0000000000..ea6b0b1e42 --- /dev/null +++ b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/03_vectorization/CMakeLists.txt @@ -0,0 +1,16 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. + +IF(Kokkos_ENABLE_CXX11) + +TRIBITS_ADD_EXECUTABLE( + tutorial_hierarchicalparallelism_03_vectorization + SOURCES vectorization.cpp + COMM serial mpi + ) + +ENDIF() + diff --git a/lib/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/CMakeLists.txt b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/CMakeLists.txt new file mode 100644 index 0000000000..15ad5d7803 --- /dev/null +++ b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/CMakeLists.txt @@ -0,0 +1,10 @@ + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +# This is a tutorial, not a test, so we don't ask CTest to run it. +TRIBITS_ADD_EXECUTABLE( + tutorial_hierarchicalparallelism_04_team_scan + SOURCES team_scan.cpp + COMM serial mpi + ) diff --git a/lib/kokkos/example/tutorial/Hierarchical_Parallelism/CMakeLists.txt b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/CMakeLists.txt new file mode 100644 index 0000000000..e03d7aeb90 --- /dev/null +++ b/lib/kokkos/example/tutorial/Hierarchical_Parallelism/CMakeLists.txt @@ -0,0 +1,8 @@ + +TRIBITS_ADD_EXAMPLE_DIRECTORIES(01_thread_teams) + +IF (Kokkos_ENABLE_CXX11) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(01_thread_teams_lambda) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(02_nested_parallel_for) + TRIBITS_ADD_EXAMPLE_DIRECTORIES(03_vectorization) +ENDIF () diff --git a/lib/kokkos/generate_makefile.bash b/lib/kokkos/generate_makefile.bash index e9e103e74d..f60bc7be8b 100755 --- a/lib/kokkos/generate_makefile.bash +++ b/lib/kokkos/generate_makefile.bash @@ -60,6 +60,9 @@ case $key in --compiler*) COMPILER="${key#*=}" ;; + --with-options*) + KOKKOS_OPT="${key#*=}" + ;; --help) echo "Kokkos configure options:" echo "--kokkos-path=/Path/To/Kokkos: Path to the Kokkos root directory" @@ -91,6 +94,8 @@ case $key in echo " KOKKOS_LDFLAGS (such as -fopenmp, -lpthread, etc.)" echo "--with-gtest=/Path/To/Gtest: set path to gtest (used in unit and performance tests" echo "--with-hwloc=/Path/To/Hwloc: set path to hwloc" + echo "--with-options=[OPTIONS]: additional options to Kokkos:" + echo " aggressive_vectorization = add ivdep on loops" exit 0 ;; *) @@ -147,6 +152,9 @@ fi if [ ${#QTHREAD_PATH} -gt 0 ]; then KOKKOS_OPTIONS="${KOKKOS_OPTIONS} QTHREAD_PATH=${QTHREAD_PATH}" fi +if [ ${#KOKKOS_OPT} -gt 0 ]; then +KOKKOS_OPTIONS="${KOKKOS_OPTIONS} KOKKOS_OPTIONS=${KOKKOS_OPT}" +fi mkdir core mkdir core/unit_test mkdir core/perf_test diff --git a/lib/kokkos/tpls/gtest/gtest/LICENSE b/lib/kokkos/tpls/gtest/gtest/LICENSE new file mode 100644 index 0000000000..1941a11f8c --- /dev/null +++ b/lib/kokkos/tpls/gtest/gtest/LICENSE @@ -0,0 +1,28 @@ +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. diff --git a/lib/kokkos/tpls/gtest/gtest/README b/lib/kokkos/tpls/gtest/gtest/README new file mode 100644 index 0000000000..82964ecc32 --- /dev/null +++ b/lib/kokkos/tpls/gtest/gtest/README @@ -0,0 +1,13 @@ +This is a fused source version of gtest 1.7.0. All that should be necessary to +start using gtest in your package is to declare the dependency and include +gtest/gtest.h. + +However, because some of the packages that are developed in Sierra do not use a +fused source version of gtest we need to make it possible for them to build with +this version as well as with their native build. To facilitate this we have +created symlinks for the other gtest headers that they use to the fused source +gtest.h. This will make it possible for them find the headers while still using +the fuse source version. This should not have any ill effects since the header is +protected and allows for only using the non-gtest.h headers in their files. + + diff --git a/lib/kokkos/tpls/gtest/gtest/gtest-all.cc b/lib/kokkos/tpls/gtest/gtest/gtest-all.cc new file mode 100644 index 0000000000..538c78db93 --- /dev/null +++ b/lib/kokkos/tpls/gtest/gtest/gtest-all.cc @@ -0,0 +1,9594 @@ +// 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. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +// 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +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. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + 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 string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// 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 // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +// 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. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is +// part of Google Test's implementation; otherwise it's undefined. +#if !GTEST_IMPLEMENTATION_ +// A user is trying to include this from his code - just say no. +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include + + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool print_time_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() : caller_frame_(NULL) {} + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_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 { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // 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 + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + +#if GTEST_HAS_PARAM_TEST + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } +#endif // GTEST_HAS_PARAM_TEST + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector test_case_indices_; + +#if GTEST_HAS_PARAM_TEST + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; +#endif // GTEST_HAS_PARAM_TEST + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +# if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +class AutoHandle { + public: + AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} + explicit AutoHandle(HANDLE handle) : handle_(handle) {} + + ~AutoHandle() { Reset(); } + + HANDLE Get() const { return handle_; } + void Reset() { Reset(INVALID_HANDLE_VALUE); } + void Reset(HANDLE handle) { + if (handle != handle_) { + if (handle_ != INVALID_HANDLE_VALUE) + ::CloseHandle(handle_); + handle_ = handle; + } + } + + private: + HANDLE handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; +# endif // GTEST_OS_WINDOWS + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : 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."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "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."); + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +// +// A user must call testing::InitGoogleTest() to initialize Google +// Test. g_init_gtest_count is set to the number of times +// InitGoogleTest() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +GTEST_API_ int g_init_gtest_count = 0; +static bool GTestIsInitialized() { return g_init_gtest_count != 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// Application pathname gotten in InitGoogleTest. +std::string g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return std::string(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return ""; + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// 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. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// 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. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() 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 +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker 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. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// 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 +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return ""; +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + +# ifdef _MSC_VER + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +# pragma warning(pop) // Restores the warning state. +# else + + _ftime64(&now); + +# endif // _MSC_VER + + return static_cast(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// 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. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// 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. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff 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. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(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& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +// 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 iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() 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. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true iff 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. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true iff 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. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff 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. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(2 * (end - start)); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// 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& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// 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& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// 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 TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// 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 value-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +#if GTEST_HAS_PARAM_TEST +void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors.GetString().c_str()); +} +#endif // GTEST_HAS_PARAM_TEST + +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. + +//Commenting out this class since its not used and wherefor produces warnings +// class TestNameIs { +// public: +// // Constructor. +// // +// // TestNameIs has NO default constructor. +// explicit TestNameIs(const char* name) +// : name_(name) {} +// +// // Returns true iff the test name of test_info matches name_. +// bool operator()(const TestInfo * test_info) const { +// return test_info && test_info->name() == name_; +// } +// +// private: +// std::string name_; +//}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { +#if GTEST_HAS_PARAM_TEST + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +#endif +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// 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* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// 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* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static std::string FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS + const bool use_color = false; +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_info.test_case_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_info.test_case_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << ms/1000.0; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + // Using non-reentrant version as localtime_r is not portable. + time_t seconds = static_cast(ms / 1000); +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe). + const struct tm* const time_struct = localtime(&seconds); // NOLINT +# pragma warning(pop) // Restores the warning state again. +#else + const struct tm* const time_struct = localtime(&seconds); // NOLINT +#endif + if (time_struct == NULL) + return ""; // Invalid ms value + + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct->tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct->tm_mday) + "T" + + String::FormatIntWidth2(time_struct->tm_hour) + ":" + + String::FormatIntWidth2(time_struct->tm_min) + ":" + + String::FormatIntWidth2(time_struct->tm_sec); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != NULL) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + + *stream << " \n"; + } + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0) + *stream << " />\n"; + else + *stream << " \n"; +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; + + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as an std::string. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; +} + +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// 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 user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(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* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return 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 TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(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 TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// 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. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// 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. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return 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 UnitTest::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_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, 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 UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +#if GTEST_HAS_PARAM_TEST +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} +#endif // GTEST_HAS_PARAM_TEST + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 + // (using this in initializer). + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +# pragma warning(pop) // Restores the warning state again. +#else + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +#endif // _MSC_VER + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), +#if GTEST_HAS_PARAM_TEST + parameterized_test_registry_(), + parameterized_tests_registered_(false), +#endif // GTEST_HAS_PARAM_TEST + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const std::string& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(test_case_name, + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // Makes sure InitGoogleTest() was called. + if (!GTestIsInitialized()) { + printf("%s", + "\nThis test program did NOT call ::testing::InitGoogleTest " + "before calling RUN_ALL_TESTS(). Please fix it.\n"); + return false; + } + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const std::string &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + &(current_test_info_->result_) : &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast(i); + } +} + +// 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. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// 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. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate an XML report in the given directory or with the given file\n" +" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + g_init_gtest_count++; + + // We don't want to run the initialization code twice. + if (g_init_gtest_count != 1) return; + + if (*argc <= 0) return; + + internal::g_executable_path = internal::StreamableToString(argv[0]); + +#if GTEST_HAS_DEATH_TEST + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#endif // GTEST_HAS_DEATH_TEST + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// 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. +void InitGoogleTest(int* argc, char** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +} // namespace testing +// 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. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +#endif // GTEST_HAS_DEATH_TEST + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// 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. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS +} + +# if !GTEST_OS_WINDOWS +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} +# else // We are not on Windows. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +# if !GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // !GTEST_OS_QNX + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy; + *result = (&dummy < ptr); +} + +bool StackGrowsDown() { + int dummy; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// 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() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// 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. +// +// Authors: keith.ray@gmail.com (Keith Ray) + + +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +//const char kPathSeparatorString[] = "\\"; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +//const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// 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 FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurence 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* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// 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 FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// 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 FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// 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 /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// 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. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// 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 FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create 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. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// 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 FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + + +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include // For TerminateProcess() +#elif GTEST_OS_WINDOWS +# include +# include +#else +# include +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_QNX +# include +# include +#endif // GTEST_OS_QNX + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_MAC + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_MAC + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// 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) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// 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. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +#endif // _MSC_VER + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); + + // Returns the size (in bytes) of a file. + static size_t GetFileSize(FILE* file); + + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +// Returns the size (in bytes) of a file. +size_t CapturedStream::GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +std::string CapturedStream::ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector g_argvs; + +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. + +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// 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. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing 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); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include +#include +#include // NOLINT +#include + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast(c); + return kAsIs; + } else { + *os << "\\x" + String::FormatHexInt(static_cast(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a wchar_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo(wc, os); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << kQuoteBegin; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// 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. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing Framework (Google Test) + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? message : + std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +// Verifies that registered_tests match the test names in +// defined_test_names_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef ::std::set::const_iterator DefinedTestIter; + registered_ = true; + + // Skip initial whitespace in registered_tests since some + // preprocessors prefix stringizied literals with whitespace. + registered_tests = SkipSpaces(registered_tests); + + Message errors; + ::std::set tests; + for (const char* names = registered_tests; names != NULL; + names = SkipComma(names)) { + const std::string name = GetPrefixUntilComma(names); + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (name == *it) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (tests.count(*it) == 0) { + errors << "You forgot to list test " << *it << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing diff --git a/lib/kokkos/tpls/gtest/gtest/gtest-test-part.h b/lib/kokkos/tpls/gtest/gtest/gtest-test-part.h new file mode 120000 index 0000000000..48d39090f1 --- /dev/null +++ b/lib/kokkos/tpls/gtest/gtest/gtest-test-part.h @@ -0,0 +1 @@ +gtest.h \ No newline at end of file diff --git a/lib/kokkos/tpls/gtest/gtest/gtest.h b/lib/kokkos/tpls/gtest/gtest/gtest.h new file mode 100644 index 0000000000..c74d098fa9 --- /dev/null +++ b/lib/kokkos/tpls/gtest/gtest/gtest.h @@ -0,0 +1,20065 @@ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing 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. + +#ifdef __GNUC__ +#pragma GCC system_header +#endif + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include + +// 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +// 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. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. +// +// 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. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// The user can define the following macros in the build script to +// control Google Test's behavior. If the user doesn't define a macro +// in this list, Google Test will define it. +// +// 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_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::string, which is different to std::string). +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::wstring, which is different to std::wstring). +// 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_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple +// is/isn't available. +// 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_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google +// Test's own tr1 tuple implementation should be +// used. Unused when the user sets +// GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. +// 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. + +// This header defines the following utilities: +// +// Macros indicating the current platform (defined to 1 if compiled on +// the given platform; otherwise undefined): +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// 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_IOS_SIMULATOR - iOS simulator +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_SYMBIAN - Symbian +// 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_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Max 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!). +// +// Note that it is possible that none of the GTEST_OS_* macros are defined. +// +// Macros indicating available Google Test features (defined to 1 if +// the corresponding feature is supported; otherwise undefined): +// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized +// tests) +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_PARAM_TEST - value-parameterized tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// 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 two are mutually exclusive. +// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). +// +// 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 operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above +// synchronization primitives have real implementations +// and Google Test is thread-safe; or 0 otherwise. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like +// platforms, 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. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// 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 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#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_ "http://code.google.com/p/googletest/" + +// 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__ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +#elif defined __SYMBIAN32__ +# define GTEST_OS_SYMBIAN 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# if TARGET_IPHONE_SIMULATOR +# define GTEST_OS_IOS_SIMULATOR 1 +# endif +# endif +#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 __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#endif // __CYGWIN__ + +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#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 +// 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 +#elif !GTEST_OS_WINDOWS_MOBILE +# include +# include +#endif + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true iff 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) +# endif +#endif + +#if 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_HAS_POSIX_RE + +#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(__BORLANDC__) +// MSVC's and C++Builder's implementations of the STL use 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(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 iff 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 iff 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 + +#if !defined(GTEST_HAS_STD_STRING) +// Even though we don't use this macro any longer, we keep it in case +// some clients still depend on it. +# define GTEST_HAS_STD_STRING 1 +#elif !GTEST_HAS_STD_STRING +// The user told us that ::std::string isn't available. +# error "Google Test cannot be used where ::std::string isn't available." +#endif // !defined(GTEST_HAS_STD_STRING) + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +# define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// is available. + +// 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)) + +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_GLOBAL_WSTRING +// The user didn't tell us whether ::wstring is available, so we need +// to figure it out. +# define GTEST_HAS_GLOBAL_WSTRING \ + (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) +#endif // GTEST_HAS_GLOBAL_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 iff 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 iff RTTI is enabled. +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) + +# 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 assume pthreads support is +// available on Linux and Mac. +// +// 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) +#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 Google Test can use tr1/tuple. You can define +// this macro to 0 to prevent Google Test from using tuple (any +// feature depending on tuple with be disabled in this mode). +#ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# else +// The user didn't tell us not to do it, so we assume it's OK. +# define GTEST_HAS_TR1_TUPLE 1 +# endif +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether Google Test's own tr1 tuple implementation +// should be used. +#ifndef GTEST_USE_OWN_TR1_TUPLE +// The user didn't tell us, so we need to figure it out. + +// We use our own TR1 tuple if we aren't sure the user has an +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif + +#endif // GTEST_USE_OWN_TR1_TUPLE + +// To avoid conditional compilation everywhere, we make it +// gtest-port.h's responsibility to #include the header implementing +// tr1/tuple. +#if GTEST_HAS_TR1_TUPLE + +# if GTEST_USE_OWN_TR1_TUPLE +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2009 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> +#define GTEST_1_TUPLE_(T) tuple +#define GTEST_2_TUPLE_(T) tuple +#define GTEST_3_TUPLE_(T) tuple +#define GTEST_4_TUPLE_(T) tuple +#define GTEST_5_TUPLE_(T) tuple +#define GTEST_6_TUPLE_(T) tuple +#define GTEST_7_TUPLE_(T) tuple +#define GTEST_8_TUPLE_(T) tuple +#define GTEST_9_TUPLE_(T) tuple +#define GTEST_10_TUPLE_(T) tuple + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. +#define GTEST_0_TYPENAMES_(T) +#define GTEST_1_TYPENAMES_(T) typename T##0 +#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 +#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 +#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3 +#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4 +#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5 +#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6 +#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 +#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8 +#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8, typename T##9 + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + +template +struct TupleElement { + typedef T0 type; +}; + +template +struct TupleElement { + typedef T1 type; +}; + +template +struct TupleElement { + typedef T2 type; +}; + +template +struct TupleElement { + typedef T3 type; +}; + +template +struct TupleElement { + typedef T4 type; +}; + +template +struct TupleElement { + typedef T5 type; +}; + +template +struct TupleElement { + typedef T6 type; +}; + +template +struct TupleElement { + typedef T7 type; +}; + +template +struct TupleElement { + typedef T8 type; +}; + +template +struct TupleElement { + typedef T9 type; +}; + +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + +template +class GTEST_1_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} + + tuple(const tuple& t) : f0_(t.f0_) {} + + template + tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_1_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { + f0_ = t.f0_; + return *this; + } + + T0 f0_; +}; + +template +class GTEST_2_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), + f1_(f1) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} + + template + tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_2_TUPLE_(U)& t) { + return CopyFrom(t); + } + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + return *this; + } + + T0 f0_; + T1 f1_; +}; + +template +class GTEST_3_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + template + tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_3_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; +}; + +template +class GTEST_4_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} + + template + tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_4_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; +}; + +template +class GTEST_5_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, + GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_) {} + + template + tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_5_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; +}; + +template +class GTEST_6_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_) {} + + template + tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_6_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; +}; + +template +class GTEST_7_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + template + tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_7_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; +}; + +template +class GTEST_8_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, + GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + template + tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_8_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; +}; + +template +class GTEST_9_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + template + tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_9_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; +}; + +template +class tuple { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), + f9_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} + + template + tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), + f9_(t.f9_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_10_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + f9_ = t.f9_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; + T9 f9_; +}; + +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +template +inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { + return GTEST_1_TUPLE_(T)(f0); +} + +template +inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { + return GTEST_2_TUPLE_(T)(f0, f1); +} + +template +inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { + return GTEST_3_TUPLE_(T)(f0, f1, f2); +} + +template +inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3) { + return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); +} + +template +inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4) { + return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); +} + +template +inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5) { + return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); +} + +template +inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6) { + return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); +} + +template +inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { + return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); +} + +template +inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8) { + return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); +} + +template +inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8, const T9& f9) { + return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); +} + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + +template +struct tuple_size { + static const int value = 0; +}; + +template +struct tuple_size { + static const int value = 1; +}; + +template +struct tuple_size { + static const int value = 2; +}; + +template +struct tuple_size { + static const int value = 3; +}; + +template +struct tuple_size { + static const int value = 4; +}; + +template +struct tuple_size { + static const int value = 5; +}; + +template +struct tuple_size { + static const int value = 6; +}; + +template +struct tuple_size { + static const int value = 7; +}; + +template +struct tuple_size { + static const int value = 8; +}; + +template +struct tuple_size { + static const int value = 9; +}; + +template +struct tuple_size { + static const int value = 10; +}; + +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + +template <> +class Get<0> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + Field(Tuple& t) { return t.f0_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + ConstField(const Tuple& t) { return t.f0_; } +}; + +template <> +class Get<1> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + Field(Tuple& t) { return t.f1_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + ConstField(const Tuple& t) { return t.f1_; } +}; + +template <> +class Get<2> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + Field(Tuple& t) { return t.f2_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + ConstField(const Tuple& t) { return t.f2_; } +}; + +template <> +class Get<3> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + Field(Tuple& t) { return t.f3_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + ConstField(const Tuple& t) { return t.f3_; } +}; + +template <> +class Get<4> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + Field(Tuple& t) { return t.f4_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + ConstField(const Tuple& t) { return t.f4_; } +}; + +template <> +class Get<5> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + Field(Tuple& t) { return t.f5_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + ConstField(const Tuple& t) { return t.f5_; } +}; + +template <> +class Get<6> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + Field(Tuple& t) { return t.f6_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + ConstField(const Tuple& t) { return t.f6_; } +}; + +template <> +class Get<7> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + Field(Tuple& t) { return t.f7_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + ConstField(const Tuple& t) { return t.f7_; } +}; + +template <> +class Get<8> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + Field(Tuple& t) { return t.f8_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + ConstField(const Tuple& t) { return t.f8_; } +}; + +template <> +class Get<9> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + Field(Tuple& t) { return t.f9_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + ConstField(const Tuple& t) { return t.f9_; } +}; + +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(const GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + +#undef GTEST_0_TUPLE_ +#undef GTEST_1_TUPLE_ +#undef GTEST_2_TUPLE_ +#undef GTEST_3_TUPLE_ +#undef GTEST_4_TUPLE_ +#undef GTEST_5_TUPLE_ +#undef GTEST_6_TUPLE_ +#undef GTEST_7_TUPLE_ +#undef GTEST_8_TUPLE_ +#undef GTEST_9_TUPLE_ +#undef GTEST_10_TUPLE_ + +#undef GTEST_0_TYPENAMES_ +#undef GTEST_1_TYPENAMES_ +#undef GTEST_2_TYPENAMES_ +#undef GTEST_3_TYPENAMES_ +#undef GTEST_4_TYPENAMES_ +#undef GTEST_5_TYPENAMES_ +#undef GTEST_6_TYPENAMES_ +#undef GTEST_7_TYPENAMES_ +#undef GTEST_8_TYPENAMES_ +#undef GTEST_9_TYPENAMES_ +#undef GTEST_10_TYPENAMES_ + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +# elif GTEST_ENV_HAS_STD_TUPLE_ +# include +// C++11 puts its tuple into the ::std namespace rather than +// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. +// This causes undefined behavior, but supported compilers react in +// the way we intend. +namespace std { +namespace tr1 { +using ::std::get; +using ::std::make_tuple; +using ::std::tuple; +using ::std::tuple_element; +using ::std::tuple_size; +} +} + +# elif GTEST_OS_SYMBIAN + +// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to +// use STLport's tuple implementation, which unfortunately doesn't +// work as the copy of STLport distributed with Symbian is incomplete. +// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to +// use its own tuple implementation. +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE + +// This prevents , which defines +// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include + +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +// GCC 4.0+ implements tr1/tuple in the header. This does +// not conform to the TR1 spec, which requires the header to be . + +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +// Until version 4.3.2, gcc has a bug that causes , +// which is #included by , to not compile when RTTI is +// disabled. _TR1_FUNCTIONAL is the header guard for +// . Hence the following #define is a hack to prevent +// from being included. +# define _TR1_FUNCTIONAL 1 +# include +# undef _TR1_FUNCTIONAL // Allows the user to #include + // if he chooses to. +# else +# include // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 + +# else +// If the compiler is not GCC 4.0+, we assume the user is using a +// spec-conforming TR1 implementation. +# include // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE + +#endif // GTEST_HAS_TR1_TUPLE + +// 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() is only available on ARM starting with Gingerbread. +# if defined(__arm__) && __ANDROID_API__ >= 9 +# 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_SYMBIAN +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// Google Test does not support death tests for VC 7.1 and earlier as +// abort() in a VC 7.1 application compiled as GUI in debug config +// 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_IOS_SIMULATOR || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX) +# define GTEST_HAS_DEATH_TEST 1 +# include // NOLINT +#endif + +// We don't support MSVC 7.1 with exceptions disabled now. Therefore +// all the compilers we care about are adequate for supporting +// value-parameterized tests. +#define GTEST_HAS_PARAM_TEST 1 + +// 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__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether to support Combine(). This only makes sense when +// value-parameterized tests are enabled. The implementation doesn't +// work on Sun Studio since it doesn't understand templated conversion +// operators. +#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) +# define GTEST_HAS_COMBINE 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_SYMBIAN || GTEST_OS_AIX) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# 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)) +#else +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// A macro to disallow operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type)\ + void operator=(type const &) + +// 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 &);\ + GTEST_DISALLOW_ASSIGN_(type) + +// 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__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC + +// 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 + +#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 + +#endif // _MSC_VER + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif + +#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(__GLIBCXX__) || defined(_LIBCPP_VERSION) +# define GTEST_HAS_CXXABI_H_ 1 +#else +# define GTEST_HAS_CXXABI_H_ 0 +#endif + +namespace testing { + +class Message; + +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_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template +struct CompileAssert { +}; + +#define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template +struct StaticAssertTypeEqHelper; + +template +struct StaticAssertTypeEqHelper {}; + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); +}; + +// Defines 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 + +#if GTEST_HAS_GLOBAL_STRING + + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + +#endif // GTEST_HAS_GLOBAL_STRING + + 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 iff regular expression re matches + // the entire str. + // PartialMatch(str, re) returns true iff regular expression re + // matches a substring of str (including str itself). + // + // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // when str contains NUL characters. + 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); + } + +#if GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const ::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#endif // GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. + 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 + + GTEST_DISALLOW_ASSIGN_(RE); +}; + +// 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); +}; + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// 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. " + +// 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 + +// 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. + if (false) { + const To to = NULL; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast(f) != NULL); +#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)); + 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 + + +#if GTEST_HAS_DEATH_TEST + +const ::std::vector& GetInjectableArgvs(); +void SetInjectableArgvs(const ::std::vector* + new_argvs); + +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector g_argvs; + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +#if GTEST_HAS_PTHREAD + +// Sleeps for (roughly) n milli-seconds. 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, NULL); +} + +// 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_, NULL)); + } + ~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); +}; + +// 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 NULL; +} + +// 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_, 0, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); + finished_ = true; + } + } + + virtual void Run() { + if (thread_can_start_ != NULL) + thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + const UserThreadFunc 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 iff we know that the thread function has finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// MutexBase and Mutex implement mutex on pthreads-based platforms. They +// are used in conjunction with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end +// // of the current scope. +// +// MutexBase implements behavior for both statically and dynamically +// allocated mutexes. Do not use MutexBase directly. Instead, write +// the following to define a static mutex: +// +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// +// You can forward declare a static mutex like this: +// +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// To create a dynamic mutex, just define an object of type Mutex. +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 } + +// 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_, NULL)); + 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 as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. 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. +// +// // 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. +// +// An object managed for a thread by a ThreadLocal instance is deleted +// when the thread exits. Or, if the ThreadLocal instance dies in +// that thread, when the ThreadLocal dies. It's the user's +// responsibility to ensure that all other threads using a ThreadLocal +// have exited when it dies, or the per-thread objects for those +// threads will not be deleted. +// +// 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: + ThreadLocal() : key_(CreateKey()), + default_() {} + explicit ThreadLocal(const T& value) : key_(CreateKey()), + default_(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: + 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 != NULL) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = new ValueHolder(default_); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + const T default_; // The default value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# define GTEST_IS_THREADSAFE 1 + +#else // GTEST_HAS_PTHREAD + +// 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 + +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class 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_; +}; + +// The above synchronization primitives have dummy implementations. +// Therefore Google Test is not thread-safe. +# define GTEST_IS_THREADSAFE 0 + +#endif // GTEST_HAS_PTHREAD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +// Passing non-POD classes through ellipsis (...) crashes the ARM +// compiler and generates a warning in Sun Studio. The Nokia Symbian +// and the IBM XL C/C++ compiler try to instantiate a copy constructor +// for objects passed through ellipsis (...), failing for uncopyable +// objects. We define this to ensure that only POD is passed through +// ellipsis on these systems. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 +#else +# define GTEST_CAN_COMPARE_NULL 1 +#endif + +// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between +// const T& and const T* in a function template. These compilers +// _can_ decide between class template specializations for T and T*, +// so a tr1::type_traits-like is_pointer works. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) +# define GTEST_NEEDS_IS_POINTER_ 1 +#endif + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +template +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +// The biggest signed integer type the compiler supports. +typedef __int64 BiggestInt; +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +typedef long long BiggestInt; // NOLINT +#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; +} +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))); +} + +// 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 IsATTY(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 IsATTY(int /* fd */) { return 0; } +# else +inline int IsATTY(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 + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int IsATTY(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 + +// Functions deprecated by MSVC 8.0. + +#ifdef _MSC_VER +// Temporarily disable warning 4996 (deprecated function). +# pragma warning(push) +# pragma warning(disable:4996) +#endif + +inline const char* StrNCpy(char* dest, const char* src, size_t n) { + return strncpy(dest, src, n); +} + +// 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 +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { + return fopen(path, mode); +} +#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 + // We are on Windows CE, which has no environment variables. + return NULL; +#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 != NULL && env[0] != '\0') ? env : NULL; +#else + return getenv(name); +#endif +} + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + +#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. +void Abort(); +#else +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 >= 1400 && !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 and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// 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. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#if GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern ::testing::internal::Int32 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_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +// Thread annotations +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) + +// 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. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_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 + +// 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing 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! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include + + +// 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; + } + +#if GTEST_OS_SYMBIAN + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // 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 == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } +#endif // GTEST_OS_SYMBIAN + + // 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 + +#if GTEST_HAS_GLOBAL_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 ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_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: + +#if GTEST_OS_SYMBIAN + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + } + template + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; + } +#endif // GTEST_OS_SYMBIAN + + // We'll hold the text streamed to this object here. + const internal::scoped_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 + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +// 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing 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 . +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include + + +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 iff 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 iff 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 iff 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 iff 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 iff 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 as "%X". + static std::string FormatHexInt(int 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 // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +// 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. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// 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 . +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + + +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 iff 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 occurence 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 + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +// This file was GENERATED by command: +// pump.py gtest-type-util.h.pump +// DO NOT EDIT BY HAND!!! + +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most 50 types in a list, and at most 50 +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_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 { + +// GetTypeName() returns a human-readable name of type T. +// 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 + + const char* const name = typeid(T).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, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return name_str; +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; +template +struct Types2 { + typedef T1 Head; + typedef Types1 Tail; +}; + +template +struct Types3 { + typedef T1 Head; + typedef Types2 Tail; +}; + +template +struct Types4 { + typedef T1 Head; + typedef Types3 Tail; +}; + +template +struct Types5 { + typedef T1 Head; + typedef Types4 Tail; +}; + +template +struct Types6 { + typedef T1 Head; + typedef Types5 Tail; +}; + +template +struct Types7 { + typedef T1 Head; + typedef Types6 Tail; +}; + +template +struct Types8 { + typedef T1 Head; + typedef Types7 Tail; +}; + +template +struct Types9 { + typedef T1 Head; + typedef Types8 Tail; +}; + +template +struct Types10 { + typedef T1 Head; + typedef Types9 Tail; +}; + +template +struct Types11 { + typedef T1 Head; + typedef Types10 Tail; +}; + +template +struct Types12 { + typedef T1 Head; + typedef Types11 Tail; +}; + +template +struct Types13 { + typedef T1 Head; + typedef Types12 Tail; +}; + +template +struct Types14 { + typedef T1 Head; + typedef Types13 Tail; +}; + +template +struct Types15 { + typedef T1 Head; + typedef Types14 Tail; +}; + +template +struct Types16 { + typedef T1 Head; + typedef Types15 Tail; +}; + +template +struct Types17 { + typedef T1 Head; + typedef Types16 Tail; +}; + +template +struct Types18 { + typedef T1 Head; + typedef Types17 Tail; +}; + +template +struct Types19 { + typedef T1 Head; + typedef Types18 Tail; +}; + +template +struct Types20 { + typedef T1 Head; + typedef Types19 Tail; +}; + +template +struct Types21 { + typedef T1 Head; + typedef Types20 Tail; +}; + +template +struct Types22 { + typedef T1 Head; + typedef Types21 Tail; +}; + +template +struct Types23 { + typedef T1 Head; + typedef Types22 Tail; +}; + +template +struct Types24 { + typedef T1 Head; + typedef Types23 Tail; +}; + +template +struct Types25 { + typedef T1 Head; + typedef Types24 Tail; +}; + +template +struct Types26 { + typedef T1 Head; + typedef Types25 Tail; +}; + +template +struct Types27 { + typedef T1 Head; + typedef Types26 Tail; +}; + +template +struct Types28 { + typedef T1 Head; + typedef Types27 Tail; +}; + +template +struct Types29 { + typedef T1 Head; + typedef Types28 Tail; +}; + +template +struct Types30 { + typedef T1 Head; + typedef Types29 Tail; +}; + +template +struct Types31 { + typedef T1 Head; + typedef Types30 Tail; +}; + +template +struct Types32 { + typedef T1 Head; + typedef Types31 Tail; +}; + +template +struct Types33 { + typedef T1 Head; + typedef Types32 Tail; +}; + +template +struct Types34 { + typedef T1 Head; + typedef Types33 Tail; +}; + +template +struct Types35 { + typedef T1 Head; + typedef Types34 Tail; +}; + +template +struct Types36 { + typedef T1 Head; + typedef Types35 Tail; +}; + +template +struct Types37 { + typedef T1 Head; + typedef Types36 Tail; +}; + +template +struct Types38 { + typedef T1 Head; + typedef Types37 Tail; +}; + +template +struct Types39 { + typedef T1 Head; + typedef Types38 Tail; +}; + +template +struct Types40 { + typedef T1 Head; + typedef Types39 Tail; +}; + +template +struct Types41 { + typedef T1 Head; + typedef Types40 Tail; +}; + +template +struct Types42 { + typedef T1 Head; + typedef Types41 Tail; +}; + +template +struct Types43 { + typedef T1 Head; + typedef Types42 Tail; +}; + +template +struct Types44 { + typedef T1 Head; + typedef Types43 Tail; +}; + +template +struct Types45 { + typedef T1 Head; + typedef Types44 Tail; +}; + +template +struct Types46 { + typedef T1 Head; + typedef Types45 Tail; +}; + +template +struct Types47 { + typedef T1 Head; + typedef Types46 Tail; +}; + +template +struct Types48 { + typedef T1 Head; + typedef Types47 Tail; +}; + +template +struct Types49 { + typedef T1 Head; + typedef Types48 Tail; +}; + +template +struct Types50 { + typedef T1 Head; + typedef Types49 Tail; +}; + + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. +template +struct Types { + typedef internal::Types50 type; +}; + +template <> +struct Types { + typedef internal::Types0 type; +}; +template +struct Types { + typedef internal::Types1 type; +}; +template +struct Types { + typedef internal::Types2 type; +}; +template +struct Types { + typedef internal::Types3 type; +}; +template +struct Types { + typedef internal::Types4 type; +}; +template +struct Types { + typedef internal::Types5 type; +}; +template +struct Types { + typedef internal::Types6 type; +}; +template +struct Types { + typedef internal::Types7 type; +}; +template +struct Types { + typedef internal::Types8 type; +}; +template +struct Types { + typedef internal::Types9 type; +}; +template +struct Types { + typedef internal::Types10 type; +}; +template +struct Types { + typedef internal::Types11 type; +}; +template +struct Types { + typedef internal::Types12 type; +}; +template +struct Types { + typedef internal::Types13 type; +}; +template +struct Types { + typedef internal::Types14 type; +}; +template +struct Types { + typedef internal::Types15 type; +}; +template +struct Types { + typedef internal::Types16 type; +}; +template +struct Types { + typedef internal::Types17 type; +}; +template +struct Types { + typedef internal::Types18 type; +}; +template +struct Types { + typedef internal::Types19 type; +}; +template +struct Types { + typedef internal::Types20 type; +}; +template +struct Types { + typedef internal::Types21 type; +}; +template +struct Types { + typedef internal::Types22 type; +}; +template +struct Types { + typedef internal::Types23 type; +}; +template +struct Types { + typedef internal::Types24 type; +}; +template +struct Types { + typedef internal::Types25 type; +}; +template +struct Types { + typedef internal::Types26 type; +}; +template +struct Types { + typedef internal::Types27 type; +}; +template +struct Types { + typedef internal::Types28 type; +}; +template +struct Types { + typedef internal::Types29 type; +}; +template +struct Types { + typedef internal::Types30 type; +}; +template +struct Types { + typedef internal::Types31 type; +}; +template +struct Types { + typedef internal::Types32 type; +}; +template +struct Types { + typedef internal::Types33 type; +}; +template +struct Types { + typedef internal::Types34 type; +}; +template +struct Types { + typedef internal::Types35 type; +}; +template +struct Types { + typedef internal::Types36 type; +}; +template +struct Types { + typedef internal::Types37 type; +}; +template +struct Types { + typedef internal::Types38 type; +}; +template +struct Types { + typedef internal::Types39 type; +}; +template +struct Types { + typedef internal::Types40 type; +}; +template +struct Types { + typedef internal::Types41 type; +}; +template +struct Types { + typedef internal::Types42 type; +}; +template +struct Types { + typedef internal::Types43 type; +}; +template +struct Types { + typedef internal::Types44 type; +}; +template +struct Types { + typedef internal::Types45 type; +}; +template +struct Types { + typedef internal::Types46 type; +}; +template +struct Types { + typedef internal::Types47 type; +}; +template +struct Types { + typedef internal::Types48 type; +}; +template +struct Types { + typedef internal::Types49 type; +}; + +namespace internal { + +# 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 + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; +template +struct Templates2 { + typedef TemplateSel Head; + typedef Templates1 Tail; +}; + +template +struct Templates3 { + typedef TemplateSel Head; + typedef Templates2 Tail; +}; + +template +struct Templates4 { + typedef TemplateSel Head; + typedef Templates3 Tail; +}; + +template +struct Templates5 { + typedef TemplateSel Head; + typedef Templates4 Tail; +}; + +template +struct Templates6 { + typedef TemplateSel Head; + typedef Templates5 Tail; +}; + +template +struct Templates7 { + typedef TemplateSel Head; + typedef Templates6 Tail; +}; + +template +struct Templates8 { + typedef TemplateSel Head; + typedef Templates7 Tail; +}; + +template +struct Templates9 { + typedef TemplateSel Head; + typedef Templates8 Tail; +}; + +template +struct Templates10 { + typedef TemplateSel Head; + typedef Templates9 Tail; +}; + +template +struct Templates11 { + typedef TemplateSel Head; + typedef Templates10 Tail; +}; + +template +struct Templates12 { + typedef TemplateSel Head; + typedef Templates11 Tail; +}; + +template +struct Templates13 { + typedef TemplateSel Head; + typedef Templates12 Tail; +}; + +template +struct Templates14 { + typedef TemplateSel Head; + typedef Templates13 Tail; +}; + +template +struct Templates15 { + typedef TemplateSel Head; + typedef Templates14 Tail; +}; + +template +struct Templates16 { + typedef TemplateSel Head; + typedef Templates15 Tail; +}; + +template +struct Templates17 { + typedef TemplateSel Head; + typedef Templates16 Tail; +}; + +template +struct Templates18 { + typedef TemplateSel Head; + typedef Templates17 Tail; +}; + +template +struct Templates19 { + typedef TemplateSel Head; + typedef Templates18 Tail; +}; + +template +struct Templates20 { + typedef TemplateSel Head; + typedef Templates19 Tail; +}; + +template +struct Templates21 { + typedef TemplateSel Head; + typedef Templates20 Tail; +}; + +template +struct Templates22 { + typedef TemplateSel Head; + typedef Templates21 Tail; +}; + +template +struct Templates23 { + typedef TemplateSel Head; + typedef Templates22 Tail; +}; + +template +struct Templates24 { + typedef TemplateSel Head; + typedef Templates23 Tail; +}; + +template +struct Templates25 { + typedef TemplateSel Head; + typedef Templates24 Tail; +}; + +template +struct Templates26 { + typedef TemplateSel Head; + typedef Templates25 Tail; +}; + +template +struct Templates27 { + typedef TemplateSel Head; + typedef Templates26 Tail; +}; + +template +struct Templates28 { + typedef TemplateSel Head; + typedef Templates27 Tail; +}; + +template +struct Templates29 { + typedef TemplateSel Head; + typedef Templates28 Tail; +}; + +template +struct Templates30 { + typedef TemplateSel Head; + typedef Templates29 Tail; +}; + +template +struct Templates31 { + typedef TemplateSel Head; + typedef Templates30 Tail; +}; + +template +struct Templates32 { + typedef TemplateSel Head; + typedef Templates31 Tail; +}; + +template +struct Templates33 { + typedef TemplateSel Head; + typedef Templates32 Tail; +}; + +template +struct Templates34 { + typedef TemplateSel Head; + typedef Templates33 Tail; +}; + +template +struct Templates35 { + typedef TemplateSel Head; + typedef Templates34 Tail; +}; + +template +struct Templates36 { + typedef TemplateSel Head; + typedef Templates35 Tail; +}; + +template +struct Templates37 { + typedef TemplateSel Head; + typedef Templates36 Tail; +}; + +template +struct Templates38 { + typedef TemplateSel Head; + typedef Templates37 Tail; +}; + +template +struct Templates39 { + typedef TemplateSel Head; + typedef Templates38 Tail; +}; + +template +struct Templates40 { + typedef TemplateSel Head; + typedef Templates39 Tail; +}; + +template +struct Templates41 { + typedef TemplateSel Head; + typedef Templates40 Tail; +}; + +template +struct Templates42 { + typedef TemplateSel Head; + typedef Templates41 Tail; +}; + +template +struct Templates43 { + typedef TemplateSel Head; + typedef Templates42 Tail; +}; + +template +struct Templates44 { + typedef TemplateSel Head; + typedef Templates43 Tail; +}; + +template +struct Templates45 { + typedef TemplateSel Head; + typedef Templates44 Tail; +}; + +template +struct Templates46 { + typedef TemplateSel Head; + typedef Templates45 Tail; +}; + +template +struct Templates47 { + typedef TemplateSel Head; + typedef Templates46 Tail; +}; + +template +struct Templates48 { + typedef TemplateSel Head; + typedef Templates47 Tail; +}; + +template +struct Templates49 { + typedef TemplateSel Head; + typedef Templates48 Tail; +}; + +template +struct Templates50 { + typedef TemplateSel Head; + typedef Templates49 Tail; +}; + + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. +template +struct Templates { + typedef Templates50 type; +}; + +template <> +struct Templates { + typedef Templates0 type; +}; +template +struct Templates { + typedef Templates1 type; +}; +template +struct Templates { + typedef Templates2 type; +}; +template +struct Templates { + typedef Templates3 type; +}; +template +struct Templates { + typedef Templates4 type; +}; +template +struct Templates { + typedef Templates5 type; +}; +template +struct Templates { + typedef Templates6 type; +}; +template +struct Templates { + typedef Templates7 type; +}; +template +struct Templates { + typedef Templates8 type; +}; +template +struct Templates { + typedef Templates9 type; +}; +template +struct Templates { + typedef Templates10 type; +}; +template +struct Templates { + typedef Templates11 type; +}; +template +struct Templates { + typedef Templates12 type; +}; +template +struct Templates { + typedef Templates13 type; +}; +template +struct Templates { + typedef Templates14 type; +}; +template +struct Templates { + typedef Templates15 type; +}; +template +struct Templates { + typedef Templates16 type; +}; +template +struct Templates { + typedef Templates17 type; +}; +template +struct Templates { + typedef Templates18 type; +}; +template +struct Templates { + typedef Templates19 type; +}; +template +struct Templates { + typedef Templates20 type; +}; +template +struct Templates { + typedef Templates21 type; +}; +template +struct Templates { + typedef Templates22 type; +}; +template +struct Templates { + typedef Templates23 type; +}; +template +struct Templates { + typedef Templates24 type; +}; +template +struct Templates { + typedef Templates25 type; +}; +template +struct Templates { + typedef Templates26 type; +}; +template +struct Templates { + typedef Templates27 type; +}; +template +struct Templates { + typedef Templates28 type; +}; +template +struct Templates { + typedef Templates29 type; +}; +template +struct Templates { + typedef Templates30 type; +}; +template +struct Templates { + typedef Templates31 type; +}; +template +struct Templates { + typedef Templates32 type; +}; +template +struct Templates { + typedef Templates33 type; +}; +template +struct Templates { + typedef Templates34 type; +}; +template +struct Templates { + typedef Templates35 type; +}; +template +struct Templates { + typedef Templates36 type; +}; +template +struct Templates { + typedef Templates37 type; +}; +template +struct Templates { + typedef Templates38 type; +}; +template +struct Templates { + typedef Templates39 type; +}; +template +struct Templates { + typedef Templates40 type; +}; +template +struct Templates { + typedef Templates41 type; +}; +template +struct Templates { + typedef Templates42 type; +}; +template +struct Templates { + typedef Templates43 type; +}; +template +struct Templates { + typedef Templates44 type; +}; +template +struct Templates { + typedef Templates45 type; +}; +template +struct Templates { + typedef Templates46 type; +}; +template +struct Templates { + typedef Templates47 type; +}; +template +struct Templates { + typedef Templates48 type; +}; +template +struct Templates { + typedef Templates49 type; +}; + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + +template +struct TypeList > { + typedef typename Types::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_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 + +class ProtocolMessage; +namespace proto2 { class Message; } + +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 cases. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// How many times InitGoogleTest() has been called. +GTEST_API_ extern int g_init_gtest_count; + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef GTEST_ELLIPSIS_NEEDS_POD_ +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_IS_NULL_LITERAL_(x) false +#else +# define GTEST_IS_NULL_LITERAL_(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // GTEST_ELLIPSIS_NEEDS_POD_ + +// 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 + +// 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); +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// A helper class for creating scoped traces in user programs. +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& 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: + 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. + +// 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 iff 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 size_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 iff 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 iff 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 case, 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: + virtual Test* CreateTest() { 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 SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// 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. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// 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_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc 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); + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// State of the definition of a type-parameterized test case. +class GTEST_API_ TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case 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_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + defined_test_names_.insert(test_name); + return true; + } + + // 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* file, int line, const char* registered_tests); + + private: + bool registered_; + ::std::set defined_test_names_; +}; + +// 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 == NULL) { + return NULL; + } + 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 == NULL ? str : std::string(str, comma); +} + +// 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_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const char* case_name, + const char* test_names, int index) { + 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 + "/" + + StreamableToString(index)).c_str(), + GetPrefixUntilComma(test_names).c_str(), + GetTypeName().c_str(), + NULL, // No value parameter. + GetTypeId(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest + ::Register(prefix, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/, int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase::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 TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase + ::Register(prefix, case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// 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; +}; + +// 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 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Defining a variable of type CompileAssertTypesEqual will cause a +// compiler error iff T1 and T2 are different types. +template +struct CompileAssertTypesEqual; + +template +struct CompileAssertTypesEqual { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template +struct RemoveReference { typedef T type; }; // NOLINT +template +struct RemoveReference { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template +struct RemoveConst { typedef T type; }; // NOLINT +template +struct RemoveConst { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template +struct AddReference { typedef T& type; }; // NOLINT +template +struct AddReference { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference::type + +// Adds a reference to const on top of T as necessary. For example, +// it transforms +// +// char ==> const char& +// const char ==> const char& +// char& ==> const char& +// const char& ==> const char& +// +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static From MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4244) // Temporarily disables warning 4244. + + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +# pragma warning(pop) // Restores the warning state. +#elif defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +#endif // _MSV_VER +}; +template +const bool ImplicitlyConvertible::value; + +// IsAProtocolMessage::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible::value || + ImplicitlyConvertible::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. +// +// Note 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 +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// EnableIf::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf::type* = 0" as the last parameter. +template struct EnableIf; +template<> struct EnableIf { typedef void type; }; // NOLINT + +// 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. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// 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. + NativeArray(const Element* array, size_t count, RelationToSource relation) { + Init(array, count, relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + static_cast(StaticAssertTypeEqHelper()); + if (relation_to_source_ == kCopy) + 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: + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t a_size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + } + size_ = a_size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + +} // namespace internal +} // namespace testing + +#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) + +// Suppresses MSVC warnings 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { statement; } + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + 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 \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtest_msg.value) + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + 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: it throws.") + +#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 +// represenation 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_case_name, test_name) \ + test_case_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, NULL, NULL, \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing 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. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +// 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + + +#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 + +// 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, const RE* regex, + 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); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// 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); + +// 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, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_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, the regex is +// ignored, and the macro must accept a streamed message even though the message +// is never printed. +# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } 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(); + +#else // 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 +// iff 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. +// +// 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 iff 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() + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_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!"); +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows), 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. +// +// TODO(wan@google.com): make thread-safe death tests search the 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 regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, 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 regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// 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); + bool operator()(int exit_status) const; + private: + // No implementation - assignment is unsupported. + void operator=(const ExitedWithCode& other); + + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS +// Tests that an exit code describes an exit due to termination by a +// given signal. +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(TestCase, 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 + +// 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 // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! + +// 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. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_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_CASE_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 case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case 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_CASE_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_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_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 + + +#if !GTEST_OS_SYMBIAN +# include +#endif + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// 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. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include +#include +#include + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2003 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. +// +// Authors: Dan Egnor (egnor@google.com) +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Bill Gibbons suggested we use something like this. +// +// Thread Safety: +// Unlike other linked_ptr implementations, in this implementation +// a linked_ptr object is thread-safe in the sense that: +// - it's safe to copy linked_ptr objects concurrently, +// - it's safe to copy *from* a linked_ptr and read its underlying +// raw pointer (e.g. via get()) concurrently, and +// - it's safe to write to two linked_ptrs that point to the same +// shared object concurrently. +// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// confusion with normal linked_ptr. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ + +#include +#include + + +namespace testing { +namespace internal { + +// Protects copying of all linked_ptr objects. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Many linked_ptr operations may change p.link_ for some linked_ptr + // variable p in the same circle as this object. Therefore we need + // to prevent two such operations from occurring concurrently. + // + // Note that different types of linked_ptr objects can coexist in a + // circle (e.g. linked_ptr, linked_ptr, and + // linked_ptr). Therefore we must use a single mutex to + // protect all linked_ptr objects. This can create serious + // contention in production code, but is acceptable in a testing + // framework. + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) p = p->next_; + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true if we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { // NOLINT + assert(&ptr != this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing 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. +// +// 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. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include // NOLINT +#include +#include +#include +#include + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// 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); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) + kOtherType // anything else +}; + +// TypeWithoutFormatter::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(reinterpret_cast(&value), + sizeof(value), os); + } +}; + +// 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. +const size_t kProtobufOneLinerMaxLength = 50; + +template +class TypeWithoutFormatter { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + const ::testing::internal::string short_str = value.ShortDebugString(); + const ::testing::internal::string pretty_str = + short_str.length() <= kProtobufOneLinerMaxLength ? + short_str : ("\n" + value.DebugString()); + *os << ("<" + pretty_str + ">"); + } +}; + +template +class TypeWithoutFormatter { + public: + // 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(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream, const Foo&) is more +// specific. +template +::std::basic_ostream& operator<<( + ::std::basic_ostream& os, const T& x) { + TypeWithoutFormatter::value ? kProtobuf : + internal::ImplicitlyConvertible::value ? + kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// 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; + +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. +template +void DefaultPrintTo(IsContainer /* dummy */, + false_type /* is not a pointer */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + 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.) +template +void DefaultPrintTo(IsNotContainer /* dummy */, + true_type /* is a pointer */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // C++ doesn't allow casting from a function pointer to any object + // pointer. + // + // IsTrue() silences warnings: "Condition is always true", + // "unreachable code". + if (IsTrue(ImplicitlyConvertible::value)) { + // 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; + } 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*. However, we cannot cast it to const void* directly, + // even using reinterpret_cast, as earlier versions of gcc + // (e.g. 3.4.5) cannot compile the cast when p is a function + // pointer. Casting to UInt64 first solves the problem. + *os << reinterpret_cast( + reinterpret_cast(p)); + } + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template +void DefaultPrintTo(IsNotContainer /* dummy */, + false_type /* is not a pointer */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// 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) { + // DefaultPrintTo() is overloaded. The type of its first two + // arguments determine which version will be picked. If T is an + // STL-style container, the version for container will be called; if + // T is a pointer, the pointer version will be called; otherwise the + // generic version will be called. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // The second argument of DefaultPrintTo() is needed to bypass a bug + // in Symbian's C++ compiler that prevents it from picking the right + // overload between: + // + // PrintTo(const T& x, ...); + // PrintTo(T* x, ...); + DefaultPrintTo(IsContainerTest(0), is_pointer(), 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); + +// 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); +} + +// 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 ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_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 ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_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_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os); + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo( + const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +// 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. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + // 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); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// 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. + // TODO(wan@google.com): let the user control the threshold using a flag. + 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); + +// 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. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + 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); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// 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 == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *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); +} + +#if GTEST_HAS_TR1_TUPLE +typedef ::std::vector Strings; + +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter. + +// The inductive case. +template +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter::PrintPrefixTo(t, os); + *os << ", "; + UniversalPrinter::type> + ::Print(::std::tr1::get(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base cases. +template <> +struct TuplePrefixPrinter<0> { + template + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; +// We have to specialize the entire TuplePrefixPrinter<> class +// template here, even though the definition of +// TersePrintPrefixToStrings() is the same as the generic version, as +// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't +// support specializing a method template of a class template. +template <> +struct TuplePrefixPrinter<1> { + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + UniversalPrinter::type>:: + Print(::std::tr1::get<0>(t), os); + } + + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<0>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + PrintPrefixTo(t, os); + *os << ")"; +} + +// 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; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Outputs a message explaining invalid registration of different +// fixture class for the same test case. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line); + +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) {} + scoped_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: + linked_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)) {} + virtual ~RangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, begin_, 0, step_); + } + virtual ParamIteratorInterface* End() const { + 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) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + value_ = value_ + step_; + index_++; + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const T* Current() const { return &value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 = 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) {} + virtual ~ValuesInIteratorRangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, container_.begin()); + } + virtual ParamIteratorInterface* End() const { + 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) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + ++iterator_; + value_.reset(); + } + virtual ParamIteratorInterface* Clone() const { + 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. + virtual const T* Current() const { + if (value_.get() == NULL) + value_.reset(new T(*iterator_)); + return value_.get(); + } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 scoped_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable scoped_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. +// +// 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) {} + virtual Test* CreateTest() { + 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 ParameterizedTestCaseInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + typedef typename TestCase::ParamType ParamType; + + TestMetaFactory() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfoBase is a generic interface +// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestCaseRegistry class holds +// a collection of pointers to the ParameterizedTestCaseInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestCaseInfoBase { + public: + virtual ~ParameterizedTestCaseInfoBase() {} + + // Base part of test case name for display purposes. + virtual const string& GetTestCaseName() const = 0; + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test case right before running them in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestCaseInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test case and generators +// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that +// test case. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestCaseInstantiation(). + typedef typename TestCase::ParamType ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + + explicit ParameterizedTestCaseInfo(const char* name) + : test_case_name_(name) {} + + // Test case base name for display purposes. + virtual const string& GetTestCaseName() const { return test_case_name_; } + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_case_name is the base name of the test case (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 case base name and DoBar is test base name. + void AddTestPattern(const char* test_case_name, + const char* test_base_name, + TestMetaFactoryBase* meta_factory) { + tests_.push_back(linked_ptr(new TestInfo(test_case_name, + test_base_name, + meta_factory))); + } + // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestCaseInstantiation(const string& instantiation_name, + GeneratorCreationFunc* func, + const char* /* file */, + int /* line */) { + instantiations_.push_back(::std::make_pair(instantiation_name, func)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test case + // test cases right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more then once. + virtual void RegisterTests() { + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + linked_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const string& instantiation_name = gen_it->first; + ParamGenerator generator((*gen_it->second)()); + + string test_case_name; + if ( !instantiation_name.empty() ) + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; + + int i = 0; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + Message test_name_stream; + test_name_stream << test_info->test_base_name << "/" << i; + MakeAndRegisterTestInfo( + test_case_name.c_str(), + test_name_stream.GetString().c_str(), + NULL, // No type parameter. + PrintToString(*param_it).c_str(), + GetTestCaseTypeId(), + TestCase::SetUpTestCase, + TestCase::TearDownTestCase, + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_case_base_name, + const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory) : + test_case_base_name(a_test_case_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory) {} + + const string test_case_base_name; + const string test_base_name; + const scoped_ptr > test_meta_factory; + }; + typedef ::std::vector > TestInfoContainer; + // Keeps pairs of + // received from INSTANTIATE_TEST_CASE_P macros. + typedef ::std::vector > + InstantiationContainer; + + const string test_case_name_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); +}; // class ParameterizedTestCaseInfo + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase +// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P +// macros use it to locate their corresponding ParameterizedTestCaseInfo +// descriptors. +class ParameterizedTestCaseRegistry { + public: + ParameterizedTestCaseRegistry() {} + ~ParameterizedTestCaseRegistry() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + delete *it; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test case. + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, + const char* file, + int line) { + ParameterizedTestCaseInfo* typed_test_info = NULL; + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + if ((*it)->GetTestCaseName() == test_case_name) { + if ((*it)->GetTestCaseTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test case setup and tear-down in this case. + ReportInvalidTestCaseType(test_case_name, file, line); + 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< + ParameterizedTestCaseInfo >(*it); + } + break; + } + } + if (typed_test_info == NULL) { + typed_test_info = new ParameterizedTestCaseInfo(test_case_name); + test_case_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + (*it)->RegisterTests(); + } + } + + private: + typedef ::std::vector TestCaseInfoContainer; + + TestCaseInfoContainer test_case_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! + +// 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. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most 50 arguments in Values, +// and at most 10 arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tr1::tuple which is +// currently set at 10. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template + operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +template +class ValueArray2 { + public: + ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray2& other); + + const T1 v1_; + const T2 v2_; +}; + +template +class ValueArray3 { + public: + ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray3& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; +}; + +template +class ValueArray4 { + public: + ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray4& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; +}; + +template +class ValueArray5 { + public: + ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray5& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; +}; + +template +class ValueArray6 { + public: + ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray6& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; +}; + +template +class ValueArray7 { + public: + ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray7& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; +}; + +template +class ValueArray8 { + public: + ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray8& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; +}; + +template +class ValueArray9 { + public: + ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray9& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; +}; + +template +class ValueArray10 { + public: + ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray10& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; +}; + +template +class ValueArray11 { + public: + ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray11& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; +}; + +template +class ValueArray12 { + public: + ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray12& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; +}; + +template +class ValueArray13 { + public: + ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray13& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; +}; + +template +class ValueArray14 { + public: + ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray14& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; +}; + +template +class ValueArray15 { + public: + ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray15& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; +}; + +template +class ValueArray16 { + public: + ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray16& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; +}; + +template +class ValueArray17 { + public: + ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray17& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; +}; + +template +class ValueArray18 { + public: + ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray18& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; +}; + +template +class ValueArray19 { + public: + ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray19& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; +}; + +template +class ValueArray20 { + public: + ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray20& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; +}; + +template +class ValueArray21 { + public: + ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray21& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; +}; + +template +class ValueArray22 { + public: + ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray22& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; +}; + +template +class ValueArray23 { + public: + ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray23& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; +}; + +template +class ValueArray24 { + public: + ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray24& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; +}; + +template +class ValueArray25 { + public: + ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray25& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; +}; + +template +class ValueArray26 { + public: + ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray26& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; +}; + +template +class ValueArray27 { + public: + ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray27& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; +}; + +template +class ValueArray28 { + public: + ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray28& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; +}; + +template +class ValueArray29 { + public: + ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray29& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; +}; + +template +class ValueArray30 { + public: + ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray30& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; +}; + +template +class ValueArray31 { + public: + ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray31& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; +}; + +template +class ValueArray32 { + public: + ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray32& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; +}; + +template +class ValueArray33 { + public: + ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray33& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; +}; + +template +class ValueArray34 { + public: + ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray34& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; +}; + +template +class ValueArray35 { + public: + ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray35& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; +}; + +template +class ValueArray36 { + public: + ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray36& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; +}; + +template +class ValueArray37 { + public: + ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray37& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; +}; + +template +class ValueArray38 { + public: + ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray38& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; +}; + +template +class ValueArray39 { + public: + ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray39& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; +}; + +template +class ValueArray40 { + public: + ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray40& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; +}; + +template +class ValueArray41 { + public: + ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray41& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; +}; + +template +class ValueArray42 { + public: + ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray42& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; +}; + +template +class ValueArray43 { + public: + ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), + v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray43& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; +}; + +template +class ValueArray44 { + public: + ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), + v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), + v43_(v43), v44_(v44) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray44& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; +}; + +template +class ValueArray45 { + public: + ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), + v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray45& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; +}; + +template +class ValueArray46 { + public: + ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray46& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; +}; + +template +class ValueArray47 { + public: + ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), + v47_(v47) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray47& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; +}; + +template +class ValueArray48 { + public: + ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), + v46_(v46), v47_(v47), v48_(v48) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray48& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; +}; + +template +class ValueArray49 { + public: + ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, + T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray49& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; +}; + +template +class ValueArray50 { + public: + ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, + T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray50& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; + const T50 v50_; +}; + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +template +class CartesianProductGenerator2 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator2(const ParamGenerator& g1, + const ParamGenerator& g2) + : g1_(g1), g2_(g2) {} + virtual ~CartesianProductGenerator2() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current2_; + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + ParamType current_value_; + }; // class CartesianProductGenerator2::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator2& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; +}; // class CartesianProductGenerator2 + + +template +class CartesianProductGenerator3 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator3(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + virtual ~CartesianProductGenerator3() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current3_; + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + ParamType current_value_; + }; // class CartesianProductGenerator3::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator3& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; +}; // class CartesianProductGenerator3 + + +template +class CartesianProductGenerator4 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator4(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + virtual ~CartesianProductGenerator4() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current4_; + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + ParamType current_value_; + }; // class CartesianProductGenerator4::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator4& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; +}; // class CartesianProductGenerator4 + + +template +class CartesianProductGenerator5 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator5(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + virtual ~CartesianProductGenerator5() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current5_; + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + ParamType current_value_; + }; // class CartesianProductGenerator5::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator5& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; +}; // class CartesianProductGenerator5 + + +template +class CartesianProductGenerator6 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator6(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + virtual ~CartesianProductGenerator6() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current6_; + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + ParamType current_value_; + }; // class CartesianProductGenerator6::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator6& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; +}; // class CartesianProductGenerator6 + + +template +class CartesianProductGenerator7 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator7(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + virtual ~CartesianProductGenerator7() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current7_; + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + ParamType current_value_; + }; // class CartesianProductGenerator7::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator7& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; +}; // class CartesianProductGenerator7 + + +template +class CartesianProductGenerator8 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator8(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + virtual ~CartesianProductGenerator8() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current8_; + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + ParamType current_value_; + }; // class CartesianProductGenerator8::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator8& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; +}; // class CartesianProductGenerator8 + + +template +class CartesianProductGenerator9 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator9(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + virtual ~CartesianProductGenerator9() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current9_; + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + ParamType current_value_; + }; // class CartesianProductGenerator9::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator9& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; +}; // class CartesianProductGenerator9 + + +template +class CartesianProductGenerator10 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator10(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9, + const ParamGenerator& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + virtual ~CartesianProductGenerator10() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end(), g10_, g10_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9, + const ParamGenerator& g10, + const typename ParamGenerator::iterator& current10) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9), + begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current10_; + if (current10_ == end10_) { + current10_ = begin10_; + ++current9_; + } + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // 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 Iterator* 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(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_ && + current10_ == typed_other->current10_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_), + begin10_(other.begin10_), + end10_(other.end10_), + current10_(other.current10_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_, *current10_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_ || + current10_ == end10_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + const typename ParamGenerator::iterator begin10_; + const typename ParamGenerator::iterator end10_; + typename ParamGenerator::iterator current10_; + ParamType current_value_; + }; // class CartesianProductGenerator10::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator10& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; + const ParamGenerator g10_; +}; // class CartesianProductGenerator10 + + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +template +class CartesianProductHolder2 { + public: +CartesianProductHolder2(const Generator1& g1, const Generator2& g2) + : g1_(g1), g2_(g2) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator2( + static_cast >(g1_), + static_cast >(g2_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder2& other); + + const Generator1 g1_; + const Generator2 g2_; +}; // class CartesianProductHolder2 + +template +class CartesianProductHolder3 { + public: +CartesianProductHolder3(const Generator1& g1, const Generator2& g2, + const Generator3& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator3( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder3& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; +}; // class CartesianProductHolder3 + +template +class CartesianProductHolder4 { + public: +CartesianProductHolder4(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator4( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder4& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; +}; // class CartesianProductHolder4 + +template +class CartesianProductHolder5 { + public: +CartesianProductHolder5(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator5( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder5& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; +}; // class CartesianProductHolder5 + +template +class CartesianProductHolder6 { + public: +CartesianProductHolder6(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator6( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder6& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; +}; // class CartesianProductHolder6 + +template +class CartesianProductHolder7 { + public: +CartesianProductHolder7(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator7( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder7& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; +}; // class CartesianProductHolder7 + +template +class CartesianProductHolder8 { + public: +CartesianProductHolder8(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator8( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder8& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; +}; // class CartesianProductHolder8 + +template +class CartesianProductHolder9 { + public: +CartesianProductHolder9(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator9( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder9& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; +}; // class CartesianProductHolder9 + +template +class CartesianProductHolder10 { + public: +CartesianProductHolder10(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9, const Generator10& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator10( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_), + static_cast >(g10_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder10& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; + const Generator10 g10_; +}; // class CartesianProductHolder10 + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case 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 case 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_CASE_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 case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// +// This instantiates tests from test case 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_CASE_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_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::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 case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to 50 parameters. +// +template +internal::ValueArray1 Values(T1 v1) { + return internal::ValueArray1(v1); +} + +template +internal::ValueArray2 Values(T1 v1, T2 v2) { + return internal::ValueArray2(v1, v2); +} + +template +internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { + return internal::ValueArray3(v1, v2, v3); +} + +template +internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { + return internal::ValueArray4(v1, v2, v3, v4); +} + +template +internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5) { + return internal::ValueArray5(v1, v2, v3, v4, v5); +} + +template +internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6) { + return internal::ValueArray6(v1, v2, v3, v4, v5, v6); +} + +template +internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7) { + return internal::ValueArray7(v1, v2, v3, v4, v5, + v6, v7); +} + +template +internal::ValueArray8 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { + return internal::ValueArray8(v1, v2, v3, v4, + v5, v6, v7, v8); +} + +template +internal::ValueArray9 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { + return internal::ValueArray9(v1, v2, v3, + v4, v5, v6, v7, v8, v9); +} + +template +internal::ValueArray10 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { + return internal::ValueArray10(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10); +} + +template +internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) { + return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); +} + +template +internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) { + return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); +} + +template +internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) { + return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); +} + +template +internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { + return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14); +} + +template +internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { + return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); +} + +template +internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16) { + return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16); +} + +template +internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17) { + return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17); +} + +template +internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18) { + return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18); +} + +template +internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { + return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); +} + +template +internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { + return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); +} + +template +internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { + return internal::ValueArray21(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); +} + +template +internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22) { + return internal::ValueArray22(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22); +} + +template +internal::ValueArray23 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23) { + return internal::ValueArray23(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23); +} + +template +internal::ValueArray24 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24) { + return internal::ValueArray24(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24); +} + +template +internal::ValueArray25 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { + return internal::ValueArray25(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25); +} + +template +internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) { + return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); +} + +template +internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) { + return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); +} + +template +internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) { + return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28); +} + +template +internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) { + return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29); +} + +template +internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { + return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30); +} + +template +internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { + return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31); +} + +template +internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32) { + return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32); +} + +template +internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33) { + return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); +} + +template +internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34) { + return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); +} + +template +internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { + return internal::ValueArray35(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); +} + +template +internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { + return internal::ValueArray36(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36); +} + +template +internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37) { + return internal::ValueArray37(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37); +} + +template +internal::ValueArray38 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38) { + return internal::ValueArray38(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, + v33, v34, v35, v36, v37, v38); +} + +template +internal::ValueArray39 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38, T39 v39) { + return internal::ValueArray39(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, + v32, v33, v34, v35, v36, v37, v38, v39); +} + +template +internal::ValueArray40 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, + T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, + T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { + return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); +} + +template +internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { + return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); +} + +template +internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) { + return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, + v42); +} + +template +internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) { + return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, + v41, v42, v43); +} + +template +internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) { + return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v40, v41, v42, v43, v44); +} + +template +internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { + return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, + v39, v40, v41, v42, v43, v44, v45); +} + +template +internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { + return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46); +} + +template +internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { + return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); +} + +template +internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, + T48 v48) { + return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, + v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); +} + +template +internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, + T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, + T47 v47, T48 v48, T49 v49) { + return internal::ValueArray49(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, + v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); +} + +template +internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, + T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, + T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { + return internal::ValueArray50(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, + v48, v49, v50); +} + +// 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 case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// 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 +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to 10 arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case 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_CASE_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. +// 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_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder2 Combine( + const Generator1& g1, const Generator2& g2) { + return internal::CartesianProductHolder2( + g1, g2); +} + +template +internal::CartesianProductHolder3 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3) { + return internal::CartesianProductHolder3( + g1, g2, g3); +} + +template +internal::CartesianProductHolder4 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4) { + return internal::CartesianProductHolder4( + g1, g2, g3, g4); +} + +template +internal::CartesianProductHolder5 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5) { + return internal::CartesianProductHolder5( + g1, g2, g3, g4, g5); +} + +template +internal::CartesianProductHolder6 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6) { + return internal::CartesianProductHolder6( + g1, g2, g3, g4, g5, g6); +} + +template +internal::CartesianProductHolder7 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7) { + return internal::CartesianProductHolder7( + g1, g2, g3, g4, g5, g6, g7); +} + +template +internal::CartesianProductHolder8 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8) { + return internal::CartesianProductHolder8( + g1, g2, g3, g4, g5, g6, g7, g8); +} + +template +internal::CartesianProductHolder9 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9) { + return internal::CartesianProductHolder9( + g1, g2, g3, g4, g5, g6, g7, g8, g9); +} + +template +internal::CartesianProductHolder10 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9, + const Generator10& g10) { + return internal::CartesianProductHolder10( + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); +} +# endif // GTEST_HAS_COMBINE + + + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ + ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + int gtest_##prefix##test_case_name##_dummy_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_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 MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +// 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. +// +// Author: mheule@google.com (Markus Heule) +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include + +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. + }; + + // 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 == NULL ? "" : 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() ? NULL : 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 iff the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != kSuccess; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + 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 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(); + virtual ~HasNewFatalFailureHelper(); + virtual void ReportTestPartResult(const TestPartResult& result); + 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 + +#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +// 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. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GTEST_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 case, 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_CASE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_CASE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test case as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + // Since we are inside a derived class template, C++ requires use 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) { ... } + +#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 case +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_CASE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test case 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 case name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_CASE_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 case name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); + +#endif // 0 + + +// Implements typed tests. + +#if GTEST_HAS_TYPED_TEST + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test case. +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define TYPED_TEST_CASE(CaseName, Types) \ + typedef ::testing::internal::TypeList< Types >::type \ + GTEST_TYPE_PARAMS_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel< \ + GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ + GTEST_TYPE_PARAMS_(CaseName)>::Register(\ + "", #CaseName, #TestName, 0); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() + +#endif // GTEST_HAS_TYPED_TEST + +// Implements type-parameterized tests. + +#if GTEST_HAS_TYPED_TEST_P + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test case are defined in. The exact +// name of the namespace is subject to change without notice. +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ + gtest_case_##TestCaseName##_ + +// 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 case. +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ + gtest_typed_test_case_p_state_##TestCaseName##_ + +// 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 case. +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ + gtest_registered_test_names_##TestCaseName##_ + +// 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_CASE_P(CaseName) \ + static ::testing::internal::TypedTestCasePState \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) + +# define TYPED_TEST_P(CaseName, TestName) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + template \ + class TestName : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ + __FILE__, __LINE__, #CaseName, #TestName); \ + } \ + template \ + void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() + +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ + __FILE__, __LINE__, #__VA_ARGS__) + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase::type>::Register(\ + #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// Depending on the platform, different string classes are available. +// On Linux, in addition to ::std::string, Google also makes use of +// class ::string, which has the same interface as ::std::string, but +// has a different implementation. +// +// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// ::string is available AND is a distinct type to ::std::string, or +// define it to 0 to indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined +// heuristically. + +namespace testing { + +// 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 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 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 the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// 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. +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); + +// 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 UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); + +} // 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 TestCase; +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); + // Used in the EXPECT_TRUE/FALSE(bool_expression). + explicit AssertionResult(bool success) : success_(success) {} + + // Returns true iff 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() != NULL ? message_->c_str() : ""; + } + // TODO(vladl@google.com): Remove this after making sure no clients use it. + // 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() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // 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. + internal::scoped_ptr< ::std::string> message_; + + GTEST_DISALLOW_ASSIGN_(AssertionResult); +}; + +// 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); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase 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 a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; + + // 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 case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true iff 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 case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. 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 + // SetUpTestCase or TearDownTestCase 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 iff the current test has the same fixture class as + // the first test in the current test case. + 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; } + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells 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 a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his 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 NULL; } + + // 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 iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test part result among all the results. i can range + // from 0 to test_property_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 TestCase; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + + // 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 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 + // testcase tags. Returns true if the property is valid. + // TODO(russr): 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_properites_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 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 case 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 case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // 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() != NULL) + return type_param_->c_str(); + return NULL; + } + + // 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() != NULL) + return value_param_->c_str(); + return NULL; + } + + // 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 case 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 iff this test will appear in the XML report. + bool is_reportable() const { + // For now, the XML report includes all tests matching the filter. + // In the future, we may trim tests that are excluded because of + // sharding. + return matches_filter_; + } + + // 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 TestCase; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc 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_case_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::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(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case 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 internal::scoped_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr value_param_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + 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 case, which consists of a vector of TestInfos. +// +// TestCase is not copyable. +class GTEST_API_ TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // 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 case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + 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 case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + 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 case. + 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 case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // 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 SetUpTestCase and TearDownTestCase. + 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 TestCase. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + 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 case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff 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 iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true iff 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 case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_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 case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his 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 NULL; } +}; + +// 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 case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + 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 case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // 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: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} + virtual void OnTestStart(const TestInfo& /*test_info*/) {} + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} + virtual void OnTestEnd(const TestInfo& /*test_info*/) {} + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} +}; + +// 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 TestCase; + 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 TestCases. +// +// 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 TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // 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; + +#if GTEST_HAS_PARAM_TEST + // Returns the ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); +#endif // GTEST_HAS_PARAM_TEST + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_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 iff the unit test passed (i.e. all test cases passed). + bool Passed() const; + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const; + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + 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 TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, 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 case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and funcions are friends as they need to access private + // members of UnitTest. + friend class Test; + friend class internal::AssertHelper; + friend class internal::ScopedTrace; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + 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); + +namespace internal { + +// 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); + +#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); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#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); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4389) // Temporarily disables warning on + // signed/unsigned mismatch. +#endif + + if (expected == actual) { + return AssertionSuccess(); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// 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. +GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // 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* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal, like NULL, false, or 0. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf::value>::type* = 0) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* expected (NULL) */, + T* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast(NULL), actual); + } +}; + +// 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. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// 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 AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +}\ +GTEST_API_ AssertionResult CmpHelper##op_name(\ + const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) + +// 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* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// 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* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// 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* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream expected_ss; + expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << expected; + + ::std::stringstream actual_ss; + actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StringStreamToString(&expected_ss), + StringStreamToString(&actual_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 + +#if GTEST_HAS_PARAM_TEST +// 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. +// } +// virtual ~FooTest() { +// // Can use GetParam() here. +// } +// virtual void SetUp() { +// // Can use GetParam() here. +// } +// virtual void TearDown { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_CASE_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. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface::GetParam()' for a test that + // uses a fixture whose parameter type is int. + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "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_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +#endif // GTEST_HAS_PARAM_TEST + +// Macros for indicating success/failure in test code. + +// 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") + +// 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 EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +// 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 10/31/2011 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// 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 " << 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 " << v1 + << "\n" << e2 << " evaluates to " << 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 " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << 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 " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << 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 " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << 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_) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {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(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), 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(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#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(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#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(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// 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(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#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 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. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles iff 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 +bool StaticAssertTypeEq() { + (void)internal::StaticAssertTypeEqHelper(); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using 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_case_name, test_name)\ + GTEST_TEST_(test_case_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_case_name, test_name) GTEST_TEST(test_case_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 case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) + +} // 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(); +} + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_