diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df9146bbcc9bbf62bbf6fc3eee5ed01bc052175c..35602445b449edd4770368300503c1c314c93778 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ -# Copyright (c) 2021-2023 Valve Corporation -# Copyright (c) 2021-2023 LunarG, Inc. +# Copyright (c) 2021-2024 Valve Corporation +# Copyright (c) 2021-2024 LunarG, Inc. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ permissions: read-all jobs: linux: + needs: codegen runs-on: ${{matrix.os}} strategy: matrix: @@ -45,7 +46,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.11' - name: Test CMake min # NOTE: The main users who benefit from an older CMake version # are linux users stuck on older LTS releases. It's idiomatic best @@ -55,9 +56,12 @@ jobs: if: ${{ matrix.os == 'ubuntu-20.04' }} uses: lukka/get-cmake@latest with: - cmakeVersion: 3.17.2 + cmakeVersion: 3.22.1 - run: sudo apt update - run: sudo apt install --yes --no-install-recommends libwayland-dev libxrandr-dev + # This is to combat a bug when using 6.6 linux kernels with thread/address sanitizer + # https://github.com/google/sanitizers/issues/1716 + - run: sudo sysctl vm.mmap_rnd_bits=28 - run: | cmake -S. -B build \ -D CMAKE_BUILD_TYPE=${{ matrix.config }} \ @@ -79,6 +83,7 @@ jobs: - run: scripts/generate_source.py --verify ext/Vulkan-Headers/registry/ linux-no-asm: + needs: codegen runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -98,6 +103,7 @@ jobs: - run: ctest --output-on-failure -E UnknownFunction --test-dir build/ linux-32: + needs: codegen runs-on: ubuntu-22.04 strategy: matrix: @@ -106,10 +112,10 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.11' - uses: lukka/get-cmake@latest with: - cmakeVersion: 3.17.2 + cmakeVersion: 3.22.1 - name: Enable 32 bit run: sudo dpkg --add-architecture i386 - run: sudo apt-get update @@ -124,6 +130,7 @@ jobs: -D UPDATE_DEPS=ON \ -D BUILD_WERROR=ON \ -D SYSCONFDIR=/etc/not_vulkan \ + -D PKG_CONFIG_EXECUTABLE=/usr/bin/i686-linux-gnu-pkg-config \ -G Ninja env: CFLAGS: -m32 @@ -136,15 +143,16 @@ jobs: working-directory: build/ linux-32-no-asm: + needs: codegen runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.11' - uses: lukka/get-cmake@latest with: - cmakeVersion: 3.17.2 + cmakeVersion: 3.22.1 - name: Enable 32 bit run: sudo dpkg --add-architecture i386 - run: sudo apt-get update @@ -159,6 +167,7 @@ jobs: -D UPDATE_DEPS=ON \ -D BUILD_WERROR=ON \ -D USE_GAS=OFF \ + -D PKG_CONFIG_EXECUTABLE=/usr/bin/i686-linux-gnu-pkg-config \ -G Ninja env: CFLAGS: -m32 @@ -170,6 +179,8 @@ jobs: working-directory: build/ windows_vs: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm runs-on: windows-latest strategy: matrix: @@ -189,6 +200,8 @@ jobs: - run: ctest --output-on-failure -C ${{matrix.config}} --test-dir build/ windows_vs-no-asm: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm runs-on: windows-latest strategy: matrix: @@ -208,6 +221,8 @@ jobs: # Test both clang and clang-cl (Chromium project uses clang-cl) windows_clang: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm runs-on: windows-2022 strategy: matrix: @@ -230,7 +245,9 @@ jobs: - run: cmake --install build --prefix build/install mac: - runs-on: macos-11 + # Mac is 10x expensive to run on GitHub machines, so only run if we know something else passed as well + needs: windows_clang + runs-on: macos-13 strategy: matrix: config: [ Debug, Release ] @@ -239,7 +256,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.11' - uses: lukka/get-cmake@latest - run: | cmake -S. -B build \ @@ -258,8 +275,10 @@ jobs: - run: ctest --output-on-failure --test-dir build/ apple-cross-compile: + # Mac is 10x expensive to run on GitHub machines, so only run if we know something else passed as well + needs: windows_clang name: ${{ matrix.CMAKE_SYSTEM_NAME }} - runs-on: macos-12 + runs-on: macos-13 strategy: matrix: CMAKE_SYSTEM_NAME: [ iOS, tvOS ] @@ -267,7 +286,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.11' - uses: lukka/get-cmake@latest - run: | cmake -S . -B build \ @@ -289,6 +308,8 @@ jobs: # Building a universal binary disables assembly automatically # Furthermore the Vulkan SDK ships universal binaries mac-univeral: + # Mac is 10x expensive to run on GitHub machines, so only run if we know something else passed as well + needs: windows_clang name: "Universal Binary Testing (STATIC ${{ matrix.static }}) w/ ${{ matrix.generator }}" runs-on: macos-latest strategy: @@ -299,7 +320,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.11' - uses: lukka/get-cmake@latest - run: | cmake -S. -B build \ @@ -322,12 +343,15 @@ jobs: vtool -show-build /tmp/lib/libvulkan.dylib | grep 'architecture arm64' chromium: + needs: codegen runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: scripts/gn/gn.py mingw: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm runs-on: windows-2022 defaults: run: @@ -336,7 +360,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - uses: lukka/get-cmake@latest - name: Setup uasm run: | @@ -353,7 +377,32 @@ jobs: - run: cmake --build build - run: cmake --install build --prefix /tmp + mingw-use-gas: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm + runs-on: windows-2022 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - uses: lukka/get-cmake@latest + - run: | + cmake -S. -B build \ + -D UPDATE_DEPS=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_WERROR=ON \ + -D USE_GAS=ON \ + -G Ninja + - run: cmake --build build + - run: cmake --install build --prefix /tmp + mingw-no-asm: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm runs-on: windows-2022 defaults: run: @@ -362,7 +411,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - uses: lukka/get-cmake@latest # Make sure this doesn't fail even without explicitly setting '-D USE_MASM=OFF' and without uasm - run: | @@ -375,6 +424,8 @@ jobs: - run: cmake --install build --prefix /tmp mingw-no-asm-explicit: + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm runs-on: windows-2022 defaults: run: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c4a12a4b89095e2df8458bdea58970b27e93bac2..dbd9c18566138be65bdf51c061bf7885b1740e9f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -53,7 +53,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -68,16 +68,16 @@ jobs: # If this step fails, then you should remove it and run the build manually - name: Autobuild if: matrix.language == 'python' - uses: github/codeql-action/autobuild@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/autobuild@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 - uses: actions/setup-python@v5 if: matrix.language == 'cpp' with: - python-version: '3.7' + python-version: '3.11' - uses: lukka/get-cmake@latest if: matrix.language == 'cpp' with: - cmakeVersion: 3.17.2 + cmakeVersion: 3.22.1 - name: Install Dependencies if: matrix.language == 'cpp' run: | @@ -96,6 +96,6 @@ jobs: run: cmake --build build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 4e7a2e087c93c23e654646a0f39a7b06e4082bb4..fd6f7410b6779e6a3e8ebaa522d2b894999e91ff 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -37,7 +37,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run clang-format - uses: jidicula/clang-format-action@v4.11.0 + uses: jidicula/clang-format-action@v4.14.0 with: clang-format-version: '16' check-path: ${{ matrix.path }} diff --git a/.gitignore b/.gitignore index 69e409b96c09d68833bfb58984a6b68409c9ac0e..ae5e0cd38b5fe79dd1e06f9507e92580ce6efe78 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ VKConfig.h *.files *.includes .vscode/ +.cache .DS_Store _out64 out32/* diff --git a/BUILD.gn b/BUILD.gn index d54845a5bcfdea2c46d1e4597283e9dd78da2e81..058727065af397d5a73d5a42d635d0353658803f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -36,6 +36,7 @@ config("vulkan_internal_config") { "-Wno-unused-variable", "-fPIC", ] + cflags_cc = [ "-std=c++17" ] ldflags = [ "-Wl,-Bsymbolic" ] @@ -54,6 +55,46 @@ config("vulkan_loader_config") { defines = [ "LOADER_USE_UNSAFE_FILE_SEARCH=1" ] } +support_unknown_function_handling = false +if (defined(ar_path) && ar_path != "" && !is_win && + (current_cpu == "arm64" || current_cpu == "x86_64")) { + support_unknown_function_handling = true + static_library("asm_offset") { + sources = [ "loader/asm_offset.c" ] + deps = [ "$vulkan_headers_dir:vulkan_headers" ] + + # Output raw assembly instead of compiled object file. The assembly will be included as a member of the output ar file. + cflags = [ "-S" ] + configs += [ ":vulkan_internal_config" ] + configs += [ ":vulkan_loader_config" ] + } + + action("gen_defines") { + script = "scripts/parse_asm_values.py" + deps = [ ":asm_offset" ] + + inputs = [ + "$target_out_dir/libasm_offset.a", + ar_path, + ] + if (current_cpu == "arm64") { + cpu = "aarch64" + } else { + cpu = "x86_64" + } + args = [ + rebase_path("$target_gen_dir/gen_defines.asm", root_build_dir), + rebase_path("$target_out_dir/libasm_offset.a", root_build_dir), + "GAS", + "Clang", + cpu, + rebase_path(ar_path, root_build_dir), + "libasm_offset.asm_offset.c.o", + ] + outputs = [ "$target_gen_dir/gen_defines.asm" ] + } +} + ohos_shared_library("vulkan_loader") { branch_protector_ret = "pac_ret" sources = [ @@ -64,7 +105,9 @@ ohos_shared_library("vulkan_loader") { "loader/cJSON.h", "loader/debug_utils.c", "loader/debug_utils.h", - "loader/dev_ext_trampoline.c", + + # Should only be linked when assembler is used + # "loader/dev_ext_trampoline.c", "loader/extension_manual.c", "loader/extension_manual.h", "loader/generated/vk_layer_dispatch_table.h", @@ -77,9 +120,13 @@ ohos_shared_library("vulkan_loader") { "loader/loader_common.h", "loader/loader_environment.c", "loader/loader_environment.h", + "loader/loader_json.c", + "loader/loader_json.h", "loader/log.c", "loader/log.h", - "loader/phys_dev_ext.c", + + # Should only be linked when assembler is used + # "loader/phys_dev_ext.c", "loader/settings.c", "loader/settings.h", "loader/stack_allocation.h", @@ -88,9 +135,6 @@ ohos_shared_library("vulkan_loader") { "loader/unknown_function_handling.c", "loader/unknown_function_handling.h", "loader/vk_loader_layer.h", - - # TODO(jmadill): Use assembler where available. - "loader/unknown_ext_chain.c", "loader/vk_loader_platform.h", "loader/wsi.c", "loader/wsi.h", @@ -112,12 +156,25 @@ ohos_shared_library("vulkan_loader") { "vulkan-headers:vulkan_headers", ] + if (support_unknown_function_handling) { + if (current_cpu == "arm64") { + sources += [ "loader/unknown_ext_chain_gas_aarch.S" ] + } else if (current_cpu == "x86_64") { + sources += [ "loader/unknown_ext_chain_gas_x86.S" ] + } else { + assert(false, "Unexpected CPU $current_cpu") + } + + defines += [ "UNKNOWN_FUNCTIONS_SUPPORTED=1" ] + deps += [ ":gen_defines" ] + include_dirs = [ "$target_gen_dir" ] + } + output_name = "vulkan" output_extension = "so" part_name = "vulkan-loader" subsystem_name = "thirdparty" - license_file = "//third_party/vulkan-loader/LICENSE.txt" } ## Build libvulkan.so }}} diff --git a/BUILD.md b/BUILD.md index 6915392204626a351825804df2872a9ea3e1f23f..9a9eef1aa084aa7090ff491be699608697f82512 100644 --- a/BUILD.md +++ b/BUILD.md @@ -16,6 +16,7 @@ Instructions for building this repository on Linux, Windows, and MacOS. - [Repository Dependencies](#repository-dependencies) - [Vulkan-Headers](#vulkan-headers) - [Test Dependencies](#test-dependencies) + - [Warnings as errors off by default!](#warnings-as-errors-off-by-default) - [Build and Install Directory Locations](#build-and-install-directory-locations) - [Building Dependent Repositories with Known-Good Revisions](#building-dependent-repositories-with-known-good-revisions) - [Automatically](#automatically) @@ -54,7 +55,6 @@ Instructions for building this repository on Linux, Windows, and MacOS. - [Cross Compilation](#cross-compilation) - [Unknown function handling which requires explicit assembly implementations](#unknown-function-handling-which-requires-explicit-assembly-implementations) - [Platforms which fully support unknown function handling](#platforms-which-fully-support-unknown-function-handling) - - [Link Time Optimization](#link-time-optimization) - [Tests](#tests) @@ -81,7 +81,7 @@ indicated by *install_dir*: ## Build Requirements 1. `C99` capable compiler -2. `CMake` version 3.17.2 or greater +2. `CMake` version 3.22.1 or greater 3. `Git` ### Test Requirements @@ -271,7 +271,7 @@ These variables should be set using the `-D` option when invoking CMake to gener - [2019](https://www.visualstudio.com/vs/older-downloads/) - The Community Edition of each of the above versions is sufficient, as well as any more capable edition. -- [CMake 3.17.2](https://cmake.org/files/v3.17/cmake-3.17.2-win64-x64.zip) is recommended. +- [CMake 3.22.1](https://cmake.org/files/v3.22.1/cmake-3.22.1-win64-x64.zip) is recommended. - Use the installer option to add CMake to the system PATH - Git Client Support - [Git for Windows](http://git-scm.com/download/win) is a popular solution @@ -382,7 +382,7 @@ This repository has been built and tested on the two most recent Ubuntu LTS versions, although earlier versions may work. It is be straightforward to adapt this repository to other Linux distributions. -[CMake 3.17.2](https://cmake.org/files/v3.17/cmake-3.17.2-Linux-x86_64.tar.gz) is recommended. +[CMake 3.22.1](https://cmake.org/files/v3.22.1/cmake-3.22.1-Linux-x86_64.tar.gz) is recommended. #### Required Package List @@ -561,7 +561,7 @@ Clone the Vulkan-Loader repository: ### MacOS build -[CMake 3.17.2](https://cmake.org/files/v3.17/cmake-3.17.2-Darwin-x86_64.tar.gz) is recommended. +[CMake 3.22.1](https://cmake.org/files/v3.22.1/cmake-3.22.1-Darwin-x86_64.tar.gz) is recommended. #### Building with the Unix Makefiles Generator @@ -633,15 +633,12 @@ can be manually disabled by setting `USE_GAS` or `USE_MASM` to `OFF`. * 64 bit Linux (x64) * 32 bit Linux (x86) * 64 bit Arm (aarch64) +* 32 bit Arm (aarch32) + Platforms not listed will use a fallback C Code path that relies on tail-call optimization to work. No guarantees are made about the use of the fallback code paths. -### Link Time Optimization - -When cross compiling, the use of Link Time Optimization (LTO) and unknown function handling -is not supported. Either LTO needs to be turned off, or the assembly should be disabled. - ## Tests To build tests, make sure that the `BUILD_TESTS` option is set to true. Using diff --git a/CMakeLists.txt b/CMakeLists.txt index 3812871ff17656c62412caf8f82794580375e6d2..97d4e0de590ab830049ee518f5135c36f84a8039 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,16 @@ # See the License for the specific language governing permissions and # limitations under the License. # ~~~ -cmake_minimum_required(VERSION 3.17.2) +cmake_minimum_required(VERSION 3.22.1) + +project(VULKAN_LOADER VERSION 1.4.309 LANGUAGES C) + +option(CODE_COVERAGE "Enable Code Coverage" OFF) +if (CODE_COVERAGE) + include(scripts/CodeCoverage.cmake) + add_code_coverage_all_targets() +endif() -project(VULKAN_LOADER VERSION 1.3.275 LANGUAGES C) # This variable enables downstream users to customize the target API # variant (e.g. Vulkan SC) @@ -104,6 +111,7 @@ elseif(APPLE) elseif(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU") option(BUILD_WSI_XCB_SUPPORT "Build XCB WSI support" ON) option(BUILD_WSI_XLIB_SUPPORT "Build Xlib WSI support" ON) + option(BUILD_WSI_XLIB_XRANDR_SUPPORT "Build X11 Xrandr WSI support" ON) option(BUILD_WSI_WAYLAND_SUPPORT "Build Wayland WSI support" ON) option(BUILD_WSI_DIRECTFB_SUPPORT "Build DirectFB WSI support" OFF) @@ -111,21 +119,31 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU") if(BUILD_WSI_XCB_SUPPORT) pkg_check_modules(XCB REQUIRED QUIET IMPORTED_TARGET xcb) + pkg_get_variable(XCB_INCLUDE_DIRS xcb includedir) target_compile_definitions(platform_wsi INTERFACE VK_USE_PLATFORM_XCB_KHR) - target_link_libraries(platform_wsi INTERFACE PkgConfig::XCB) + target_include_directories(platform_wsi INTERFACE ${XCB_INCLUDE_DIRS}) endif() if(BUILD_WSI_XLIB_SUPPORT) pkg_check_modules(X11 REQUIRED QUIET IMPORTED_TARGET x11) - target_compile_definitions(platform_wsi INTERFACE VK_USE_PLATFORM_XLIB_KHR VK_USE_PLATFORM_XLIB_XRANDR_EXT) - target_link_libraries(platform_wsi INTERFACE PkgConfig::X11) + pkg_get_variable(XLIB_INCLUDE_DIRS x11 includedir) + target_compile_definitions(platform_wsi INTERFACE VK_USE_PLATFORM_XLIB_KHR) + target_include_directories(platform_wsi INTERFACE ${XLIB_INCLUDE_DIRS}) + if(BUILD_WSI_XLIB_XRANDR_SUPPORT) + pkg_check_modules(XRANDR REQUIRED QUIET IMPORTED_TARGET xrandr) + pkg_get_variable(XLIB_XRANDR_INCLUDE_DIRS xrandr includedir) + target_compile_definitions(platform_wsi INTERFACE VK_USE_PLATFORM_XLIB_XRANDR_EXT) + target_include_directories(platform_wsi INTERFACE ${XLIB_XRANDR_INCLUDE_DIRS}) + endif() endif() if(BUILD_WSI_WAYLAND_SUPPORT) target_compile_definitions(platform_wsi INTERFACE VK_USE_PLATFORM_WAYLAND_KHR) endif() if(BUILD_WSI_DIRECTFB_SUPPORT) pkg_check_modules(DirectFB QUIET REQUIRED IMPORTED_TARGET directfb) + pkg_get_variable(DIRECTFB_INCLUDE_DIRS directfb includedir) target_compile_definitions(platform_wsi INTERFACE VK_USE_PLATFORM_DIRECTFB_EXT) - target_link_libraries(platform_wsi INTERFACE PkgConfig::DirectFB) + # vulkan_core.h includes but the header is installed to directfb/directfb.h + target_include_directories(platform_wsi INTERFACE ${DIRECTFB_INCLUDE_DIRS} ${DIRECTFB_INCLUDE_DIRS}/directfb) endif() elseif(CMAKE_SYSTEM_NAME MATCHES "QNX") message(FATAL_ERROR "See BUILD.md for QNX build") @@ -141,6 +159,8 @@ target_link_libraries(loader_common_options INTERFACE platform_wsi) # Enable beta Vulkan extensions target_compile_definitions(loader_common_options INTERFACE VK_ENABLE_BETA_EXTENSIONS) +string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" SYSTEM_PROCESSOR) + option(BUILD_WERROR "Enable warnings as errors") # Set warnings as errors and the main diagnostic flags @@ -148,28 +168,46 @@ option(BUILD_WERROR "Enable warnings as errors") # Note that clang-cl.exe should use MSVC flavor flags, not GNU if (CMAKE_C_COMPILER_ID STREQUAL "MSVC" OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "MSVC")) if (BUILD_WERROR) - target_compile_options(loader_common_options INTERFACE /WX) + target_compile_options(loader_common_options INTERFACE $<$:/WX>) endif() - target_compile_options(loader_common_options INTERFACE /W4) + target_compile_options(loader_common_options INTERFACE $<$:/W4>) elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") # using GCC or Clang with the regular front end if (BUILD_WERROR) - target_compile_options(loader_common_options INTERFACE -Werror) + target_compile_options(loader_common_options INTERFACE $<$:-Werror>) endif() - target_compile_options(loader_common_options INTERFACE -Wall -Wextra) + target_compile_options(loader_common_options INTERFACE + $<$:-Wall> + $<$:-Wextra> + ) endif() if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(loader_common_options INTERFACE -Wno-missing-field-initializers) + target_compile_options(loader_common_options INTERFACE $<$:-Wno-missing-field-initializers>) + + # need to prepend /clang: to compiler arguments when using clang-cl + if (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND "${CMAKE_C_COMPILER_FRONTEND_VARIANT}" MATCHES "MSVC") + target_compile_options(loader_common_options INTERFACE $<$:/clang:-fno-strict-aliasing>) + else() + target_compile_options(loader_common_options INTERFACE $<$:-fno-strict-aliasing>) + endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - target_compile_options(loader_common_options INTERFACE -Wno-stringop-truncation -Wno-stringop-overflow) + target_compile_options(loader_common_options INTERFACE + $<$:-Wno-stringop-truncation> + $<$:-Wno-stringop-overflow> + ) if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 7.1) - target_compile_options(loader_common_options INTERFACE -Wshadow=local) #only added in GCC 7 + target_compile_options(loader_common_options INTERFACE $<$:-Wshadow=local>) #only added in GCC 7 endif() endif() - target_compile_options(loader_common_options INTERFACE -Wpointer-arith) + target_compile_options(loader_common_options INTERFACE $<$:-Wpointer-arith>) + + # Force GLIBC to use the 64 bit interface for file operations instead of 32 bit - More info in issue #1551 + if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") + target_compile_definitions(loader_common_options INTERFACE _FILE_OFFSET_BITS=64) + endif() endif() if(CMAKE_C_COMPILER_ID MATCHES "MSVC" OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "MSVC")) @@ -178,13 +216,24 @@ if(CMAKE_C_COMPILER_ID MATCHES "MSVC" OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" A # /guard:cf: Enable control flow guard # /wd4152: Disable warning on conversion of a function pointer to a data pointer # /wd4201: Disable warning on anonymous struct/unions - target_compile_options(loader_common_options INTERFACE /sdl /GR- /guard:cf /wd4152 /wd4201) + target_compile_options(loader_common_options INTERFACE + $<$:/sdl> + $<$:/GR-> + $<$:/guard:cf> + $<$:/wd4152> + $<$:/wd4201> + ) # Enable control flow guard target_link_options(loader_common_options INTERFACE "LINKER:/guard:cf") # Prevent from polluting the code. guards against things like MIN and MAX target_compile_definitions(loader_common_options INTERFACE WIN32_LEAN_AND_MEAN) + + # For some reason Advapi32.lib needs to be explicitely linked to when building for Arm (32 bit) on Windows, but isn't required on any other architecture + if (SYSTEM_PROCESSOR MATCHES "arm" AND CMAKE_SIZEOF_VOID_P EQUAL 4) + target_link_libraries(loader_common_options INTERFACE Advapi32) + endif() endif() # DEBUG enables runtime loader ICD verification diff --git a/README.OpenSource b/README.OpenSource index 365652e911ebbe927218f8ffd715992890e1f762..0ca70381ce52f9ab0d29cb8d654bc5b5103f4d8b 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name": "Khronos Group - Vulkan-Loader", "License": "Apache-2.0", "License File": "LICENSE", - "Version Number": "v1.3.275", + "Version Number": "v1.4.309", "Owner": "wangxutao5@huawei.com", "Upstream URL": "https://github.com/KhronosGroup/Vulkan-Loader.git", "Description": "This project provides the Vulkan ICD desktop loader for Windows, Linux, MacOS, and OpenHarmony." diff --git a/README_OpenHarmony.md b/README_OpenHarmony.md index 5e077d2917b4e0b33926edecfdafa12597b9bffa..c17e46f9e7f4699792f59b0ae9ac3ada5027a430 100644 --- a/README_OpenHarmony.md +++ b/README_OpenHarmony.md @@ -41,7 +41,7 @@ Vulkan-Loader会根据GPU驱动的**json清单文件**中配置的`library_path` "file_format_version": "1.0.1", "ICD": { "library_path": "path to driver library", - "api_version": "1.3.275", + "api_version": "1.4.309", "library_arch" : "64", "is_portability_driver": false } diff --git a/docs/LoaderApplicationInterface.md b/docs/LoaderApplicationInterface.md index d8454f1fb02c89d18df9ceab0813b2e0dab92a3f..c05f2f46bdc1495b0a63cd1631b7ae0f74cf04af 100644 --- a/docs/LoaderApplicationInterface.md +++ b/docs/LoaderApplicationInterface.md @@ -472,18 +472,23 @@ This can be accomplished in one of two ways: [VkConfig](https://github.com/LunarG/VulkanTools/blob/main/vkconfig/README.md) tool shipped with the Vulkan SDK. 2. Directing the loader to look for layers in specific files and/or folders by using the -`VK_LAYER_PATH` environment variable. +`VK_LAYER_PATH` and/or `VK_IMPLICIT_LAYER_PATH` environment variables. -The `VK_LAYER_PATH` environment variable can contain multiple paths separated by -the operating-system specific path separator. +The `VK_LAYER_PATH` and `VK_IMPLICIT_LAYER_PATH` environment variables can contain multiple +paths separated by the operating-system specific path separator. On Windows, this is a semicolon (`;`), while on Linux and macOS it is a colon (`:`). If `VK_LAYER_PATH` exists, the files and/or folders listed will be scanned for explicit layer manifest files. Implicit layer discovery is unaffected by this environment variable. -Each directory listed should be the full pathname of a folder containing layer -manifest files. + +If `VK_IMPLICIT_LAYER_PATH` exists, the files and/or folders listed will be scanned for +implicit layer manifest files. +Explicit layer discovery is unaffected by this environment variable. + +Each directory listed in `VK_LAYER_PATH` and `VK_IMPLICIT_LAYER_PATH` should be the full +pathname of a folder containing layer manifest files. See the [Table of Debug Environment Variables](LoaderInterfaceArchitecture.md#table-of-debug-environment-variables) @@ -493,9 +498,9 @@ for more details. #### Exception for Elevated Privileges -For security reasons, `VK_LAYER_PATH` is ignored if running with elevated -privileges. -Because of this, `VK_LAYER_PATH` can only be used for applications that do not +For security reasons, `VK_LAYER_PATH` and `VK_IMPLICIT_LAYER_PATH` are ignored if running +with elevated privileges. +Because of this, the environment variables can only be used for applications that do not use elevated privileges. For more information see diff --git a/docs/LoaderDebugging.md b/docs/LoaderDebugging.md index 47d5ae2ed69cc9fe17dc42304376124d1fafe332..343e6f728a11ee7c6d3fb173dd849c09590f0fa9 100644 --- a/docs/LoaderDebugging.md +++ b/docs/LoaderDebugging.md @@ -99,57 +99,59 @@ For example, the output of the loader looking for implicit layers may look like the following: ``` -LAYER: Searching for layer manifest files -LAYER: In following locations: -LAYER: /home/${USER}/.config/vulkan/implicit_layer.d -LAYER: /etc/xdg/vulkan/implicit_layer.d -LAYER: /usr/local/etc/vulkan/implicit_layer.d -LAYER: /etc/vulkan/implicit_layer.d -LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d -LAYER: /home/${USER}/.local/share/flatpak/exports/share/vulkan/implicit_layer.d -LAYER: /var/lib/flatpak/exports/share/vulkan/implicit_layer.d -LAYER: /usr/local/share/vulkan/implicit_layer.d -LAYER: /usr/share/vulkan/implicit_layer.d -LAYER: Found the following files: -LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/renderdoc_capture.json -LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamfossilize_i386.json -LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamfossilize_x86_64.json -LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamoverlay_i386.json -LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamoverlay_x86_64.json -LAYER: /usr/share/vulkan/implicit_layer.d/nvidia_layers.json -LAYER: /usr/share/vulkan/implicit_layer.d/VkLayer_MESA_device_select.json +[Vulkan Loader] LAYER: Searching for implicit layer manifest files +[Vulkan Loader] LAYER: In following locations: +[Vulkan Loader] LAYER: /home/${USER}/.config/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /etc/xdg/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /usr/local/etc/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /etc/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /home/${USER}/.local/share/flatpak/exports/share/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /var/lib/flatpak/exports/share/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /usr/local/share/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: /usr/share/vulkan/implicit_layer.d +[Vulkan Loader] LAYER: Found the following files: +[Vulkan Loader] LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/renderdoc_capture.json +[Vulkan Loader] LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamfossilize_i386.json +[Vulkan Loader] LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamfossilize_x86_64.json +[Vulkan Loader] LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamoverlay_i386.json +[Vulkan Loader] LAYER: /home/${USER}/.local/share/vulkan/implicit_layer.d/steamoverlay_x86_64.json +[Vulkan Loader] LAYER: /usr/share/vulkan/implicit_layer.d/nvidia_layers.json +[Vulkan Loader] LAYER: /usr/share/vulkan/implicit_layer.d/VkLayer_MESA_device_select.json ``` Then, the loading of layer libraries is reported similar to this: ``` -LAYER | DEBUG: Loading layer library libVkLayer_khronos_validation.so -LAYER | INFO: Insert instance layer VK_LAYER_KHRONOS_validation (libVkLayer_khronos_validation.so) -LAYER | DEBUG: Loading layer library libVkLayer_MESA_device_select.so -LAYER | INFO: Insert instance layer VK_LAYER_MESA_device_select (libVkLayer_MESA_device_select.so) +[Vulkan Loader] DEBUG | LAYER : Loading layer library libVkLayer_khronos_validation.so +[Vulkan Loader] INFO | LAYER : Insert instance layer VK_LAYER_KHRONOS_validation (libVkLayer_khronos_validation.so) +[Vulkan Loader] DEBUG | LAYER : Loading layer library libVkLayer_MESA_device_select.so +[Vulkan Loader] INFO | LAYER : Insert instance layer VK_LAYER_MESA_device_select (libVkLayer_MESA_device_select.so) ``` Finally, when the Vulkan instance is created, you can see the full instance call-chain from a functional standpoint with output like this: ``` -LAYER: vkCreateInstance layer callstack setup to: -LAYER: -LAYER: || -LAYER: -LAYER: || -LAYER: VK_LAYER_MESA_device_select -LAYER: Type: Implicit -LAYER: Disable Env Var: NODEVICE_SELECT -LAYER: Manifest: /usr/share/vulkan/implicit_layer.d/VkLayer_MESA_device_select.json -LAYER: Library: libVkLayer_MESA_device_select.so -LAYER: || -LAYER: VK_LAYER_KHRONOS_validation -LAYER: Type: Explicit -LAYER: Manifest: /usr/share/vulkan/explicit_layer.d/VkLayer_khronos_validation.json -LAYER: Library: libVkLayer_khronos_validation.so -LAYER: || -LAYER: +[Vulkan Loader] LAYER: vkCreateInstance layer callstack setup to: +[Vulkan Loader] LAYER: +[Vulkan Loader] LAYER: || +[Vulkan Loader] LAYER: +[Vulkan Loader] LAYER: || +[Vulkan Loader] LAYER: VK_LAYER_MESA_device_select +[Vulkan Loader] LAYER: Type: Implicit +[Vulkan Loader] LAYER: Enabled By: Implicit Layer +[Vulkan Loader] LAYER: Disable Env Var: NODEVICE_SELECT +[Vulkan Loader] LAYER: Manifest: /usr/share/vulkan/implicit_layer.d/VkLayer_MESA_device_select.json +[Vulkan Loader] LAYER: Library: libVkLayer_MESA_device_select.so +[Vulkan Loader] LAYER: || +[Vulkan Loader] LAYER: VK_LAYER_KHRONOS_validation +[Vulkan Loader] LAYER: Type: Explicit +[Vulkan Loader] LAYER: Enabled By: By the Application +[Vulkan Loader] LAYER: Manifest: /usr/share/vulkan/explicit_layer.d/VkLayer_khronos_validation.json +[Vulkan Loader] LAYER: Library: libVkLayer_khronos_validation.so +[Vulkan Loader] LAYER: || +[Vulkan Loader] LAYER: ``` In this scenario, two layers were used (the same two that were loaded earlier): @@ -186,9 +188,9 @@ This will disable all implicit layers and the loader will report any disabled layers to the logging output when layer logging is enabled in the following way: ``` -WARNING | LAYER: Implicit layer "VK_LAYER_MESA_device_select" forced disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE'. -WARNING | LAYER: Implicit layer "VK_LAYER_AMD_switchable_graphics_64" forced disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE'. -WARNING | LAYER: Implicit layer "VK_LAYER_Twitch_Overlay" forced disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE'. +[Vulkan Loader] WARNING | LAYER: Implicit layer "VK_LAYER_MESA_device_select" forced disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE'. +[Vulkan Loader] WARNING | LAYER: Implicit layer "VK_LAYER_AMD_switchable_graphics_64" forced disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE'. +[Vulkan Loader] WARNING | LAYER: Implicit layer "VK_LAYER_Twitch_Overlay" forced disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE'. ``` ### Selectively Re-enable Layers @@ -283,34 +285,34 @@ look like the following (NOTE: additional spaces have been removed from the output for easier reading): ``` -DRIVER: Searching for driver manifest files -DRIVER: In following folders: -DRIVER: /home/$(USER)/.config/vulkan/icd.d -DRIVER: /etc/xdg/vulkan/icd.d -DRIVER: /etc/vulkan/icd.d -DRIVER: /home/$(USER)/.local/share/vulkan/icd.d -DRIVER: /home/$(USER)/.local/share/flatpak/exports/share/vulkan/icd.d -DRIVER: /var/lib/flatpak/exports/share/vulkan/icd.d -DRIVER: /usr/local/share/vulkan/icd.d -DRIVER: /usr/share/vulkan/icd.d -DRIVER: Found the following files: -DRIVER: /usr/share/vulkan/icd.d/intel_icd.x86_64.json -DRIVER: /usr/share/vulkan/icd.d/lvp_icd.x86_64.json -DRIVER: /usr/share/vulkan/icd.d/radeon_icd.x86_64.json -DRIVER: /usr/share/vulkan/icd.d/lvp_icd.i686.json -DRIVER: /usr/share/vulkan/icd.d/radeon_icd.i686.json -DRIVER: /usr/share/vulkan/icd.d/intel_icd.i686.json -DRIVER: /usr/share/vulkan/icd.d/nvidia_icd.json -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/intel_icd.x86_64.json, version "1.0.0" -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/lvp_icd.x86_64.json, version "1.0.0" -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/radeon_icd.x86_64.json, version "1.0.0" -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/lvp_icd.i686.json, version "1.0.0" -DRIVER: Requested driver /usr/lib/libvulkan_lvp.so was wrong bit-type. Ignoring this JSON -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/radeon_icd.i686.json, version "1.0.0" -DRIVER: Requested driver /usr/lib/libvulkan_radeon.so was wrong bit-type. Ignoring this JSON -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/intel_icd.i686.json, version "1.0.0" -DRIVER: Requested driver /usr/lib/libvulkan_intel.so was wrong bit-type. Ignoring this JSON -DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/nvidia_icd.json, version "1.0.0" +[Vulkan Loader] DRIVER: Searching for driver manifest files +[Vulkan Loader] DRIVER: In following folders: +[Vulkan Loader] DRIVER: /home/$(USER)/.config/vulkan/icd.d +[Vulkan Loader] DRIVER: /etc/xdg/vulkan/icd.d +[Vulkan Loader] DRIVER: /etc/vulkan/icd.d +[Vulkan Loader] DRIVER: /home/$(USER)/.local/share/vulkan/icd.d +[Vulkan Loader] DRIVER: /home/$(USER)/.local/share/flatpak/exports/share/vulkan/icd.d +[Vulkan Loader] DRIVER: /var/lib/flatpak/exports/share/vulkan/icd.d +[Vulkan Loader] DRIVER: /usr/local/share/vulkan/icd.d +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d +[Vulkan Loader] DRIVER: Found the following files: +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/intel_icd.x86_64.json +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/lvp_icd.x86_64.json +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/radeon_icd.x86_64.json +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/lvp_icd.i686.json +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/radeon_icd.i686.json +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/intel_icd.i686.json +[Vulkan Loader] DRIVER: /usr/share/vulkan/icd.d/nvidia_icd.json +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/intel_icd.x86_64.json, version "1.0.0" +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/lvp_icd.x86_64.json, version "1.0.0" +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/radeon_icd.x86_64.json, version "1.0.0" +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/lvp_icd.i686.json, version "1.0.0" +[Vulkan Loader] DRIVER: Requested driver /usr/lib/libvulkan_lvp.so was wrong bit-type. Ignoring this JSON +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/radeon_icd.i686.json, version "1.0.0" +[Vulkan Loader] DRIVER: Requested driver /usr/lib/libvulkan_radeon.so was wrong bit-type. Ignoring this JSON +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/intel_icd.i686.json, version "1.0.0" +[Vulkan Loader] DRIVER: Requested driver /usr/lib/libvulkan_intel.so was wrong bit-type. Ignoring this JSON +[Vulkan Loader] DRIVER: Found ICD manifest file /usr/share/vulkan/icd.d/nvidia_icd.json, version "1.0.0" ``` Then when the application selects the device to use, you will see the Vulkan @@ -318,13 +320,13 @@ device call chain reported in the following way (NOTE: additional spaces have been removed from the output for easier reading): ``` -DRIVER: vkCreateDevice layer callstack setup to: -DRIVER: -DRIVER: || -DRIVER: -DRIVER: || -DRIVER: -DRIVER: Using "Intel(R) UHD Graphics 630 (CFL GT2)" with driver: "/usr/lib64/libvulkan_intel.so" +[Vulkan Loader] DRIVER: vkCreateDevice layer callstack setup to: +[Vulkan Loader] DRIVER: +[Vulkan Loader] DRIVER: || +[Vulkan Loader] DRIVER: +[Vulkan Loader] DRIVER: || +[Vulkan Loader] DRIVER: +[Vulkan Loader] DRIVER: Using "Intel(R) UHD Graphics 630 (CFL GT2)" with driver: "/usr/lib64/libvulkan_intel.so" ``` @@ -351,8 +353,8 @@ The loader outputs messages like the following when the environment variables are used: ``` -WARNING | DRIVER: Driver "intel_icd.x86_64.json" ignored because not selected by env var 'VK_LOADER_DRIVERS_SELECT' -WARNING | DRIVER: Driver "radeon_icd.x86_64.json" ignored because it was disabled by env var 'VK_LOADER_DRIVERS_DISABLE' +[Vulkan Loader] WARNING | DRIVER: Driver "intel_icd.x86_64.json" ignored because not selected by env var 'VK_LOADER_DRIVERS_SELECT' +[Vulkan Loader] WARNING | DRIVER: Driver "radeon_icd.x86_64.json" ignored because it was disabled by env var 'VK_LOADER_DRIVERS_DISABLE' ``` For more info on how to use the filtering environment variables, refer to the diff --git a/docs/LoaderDriverInterface.md b/docs/LoaderDriverInterface.md index c8e1872fe71f711bc94bbb62a3e918a4041b8753..791c64c1c47796d6dc7df8d36d4ab5445f1d4e7c 100644 --- a/docs/LoaderDriverInterface.md +++ b/docs/LoaderDriverInterface.md @@ -172,7 +172,7 @@ message will show for each driver that has been ignored. This message will look like the following: ``` -WARNING | DRIVER: Driver "intel_icd.x86_64.json" ignored because not selected by env var 'VK_LOADER_DRIVERS_SELECT' +[Vulkan Loader] WARNING | DRIVER: Driver "intel_icd.x86_64.json" ignored because not selected by env var 'VK_LOADER_DRIVERS_SELECT' ``` If no drivers are found with a manifest filename that matches any of the @@ -190,7 +190,7 @@ will show for each driver that has been forcibly disabled. This message will look like the following: ``` -WARNING | DRIVER: Driver "radeon_icd.x86_64.json" ignored because it was disabled by env var 'VK_LOADER_DRIVERS_DISABLE' +[Vulkan Loader] WARNING | DRIVER: Driver "radeon_icd.x86_64.json" ignored because it was disabled by env var 'VK_LOADER_DRIVERS_DISABLE' ``` If no drivers are found with a manifest filename that matches any of the @@ -360,7 +360,7 @@ AppX/MSIX packages. If a package is found, the loader will scan the root directory of this installed package for JSON manifest files. At this time, the only package that is known is Microsoft's -[OpenCL™ and OpenGL® Compatibility Pack](https://apps.microsoft.com/store/detail/9NQPSL29BFFF?hl=en-us&gl=US). +[OpenCL™, OpenGL®, and Vulkan® Compatibility Pack](https://apps.microsoft.com/store/detail/9NQPSL29BFFF?hl=en-us&gl=US). The Vulkan loader will open each enabled manifest file found to obtain the name or pathname of a driver's shared library (".DLL") file. diff --git a/docs/LoaderInterfaceArchitecture.md b/docs/LoaderInterfaceArchitecture.md index dc9347b990ba3dc3a4ddf8df3aa8e403b2bf178a..a1bf4c8faa410ab362c8bbe300c029f18efd7fc1 100644 --- a/docs/LoaderInterfaceArchitecture.md +++ b/docs/LoaderInterfaceArchitecture.md @@ -202,7 +202,7 @@ These files are found in different locations based on your platform: Vulkan Layer Settings - (registry) HKEY_CURRENT_USER\Software\Khronos\Vulkan\Settings + (registry) HKEY_CURRENT_USER\Software\Khronos\Vulkan\LoaderSettings VkConfig Configuration Settings @@ -430,6 +430,8 @@ These behaviors also result in ignoring certain environment variables, such as: * `VK_ADD_DRIVER_FILES` * `VK_LAYER_PATH` * `VK_ADD_LAYER_PATH` + * `VK_IMPLICIT_LAYER_PATH` + * `VK_ADD_IMPLICIT_LAYER_PATH` * `XDG_CONFIG_HOME` (Linux/Mac-specific) * `XDG_DATA_HOME` (Linux/Mac-specific) @@ -606,8 +608,8 @@ discovery. Provide a list of additional paths that the loader will use to search - for layers in addition to the loader's standard Layer library search - folder when looking for explicit layer manifest files. + for explicit layers in addition to the loader's standard layer library + search paths when looking for layer manifest files. The paths will be added first, prior to the list of folders that would be searched normally. @@ -624,6 +626,31 @@ discovery.   VK_ADD_LAYER_PATH=
     <path_a>;<path_b> + + + + VK_ADD_IMPLICIT_LAYER_PATH + + + Provide a list of additional paths that the loader will use to search + for implicit layers in addition to the loader's standard layer library + search paths when looking for layer manifest files. + The paths will be added first, prior to the list of folders that would + be searched normally. + + + + Ignored when running Vulkan application with elevated privileges. + + + + export
+   VK_ADD_IMPLICIT_LAYER_PATH=
+      <path_a>:<path_b>

+ set
+   VK_ADD_IMPLICIT_LAYER_PATH=
+      <path_a>;<path_b>
+ @@ -640,7 +667,11 @@ discovery. continue to work. - If a relative path not used, issues may be encountered. + This functionality is only available with Loaders built with version + 1.3.207 of the Vulkan headers and later.
+ It is recommended to use absolute paths to JSON files. + Relative paths may have issues due to how the loader transforms relative library + paths into absolute ones.

Ignored when running Vulkan application with elevated privileges. @@ -661,8 +692,8 @@ discovery. VK_LAYER_PATH - Override the loader's standard Layer library search folders and use the - provided delimited file and/or folders to locate explicit layer manifest files. + Override the loader's standard explicit layer search paths and use the + provided delimited files and/or folders to locate layer manifest files. @@ -678,6 +709,27 @@ discovery.      <path_a>;<path_b> + + + VK_IMPLICIT_LAYER_PATH + + Override the loader's standard implicit layer search paths and use the + provided delimited files and/or folders to locate layer manifest files. + + + + Ignored when running Vulkan application with elevated privileges. + + + + export
+   VK_IMPLICIT_LAYER_PATH=
+      <path_a>:<path_b>

+ set
+   VK_IMPLICIT_LAYER_PATH=
+      <path_a>;<path_b> +
+ VK_LOADER_DEBUG diff --git a/docs/LoaderLayerInterface.md b/docs/LoaderLayerInterface.md index 2ee6144197860f8580064de7389689d68804ee14..ae640c95eb45034a78c21d2568a86a817c91fcfd 100644 --- a/docs/LoaderLayerInterface.md +++ b/docs/LoaderLayerInterface.md @@ -233,6 +233,13 @@ distributed as part of a driver installation. An application installer should not modify the device-specific registries, while a device driver should not modify the system registries. +Additionally, the Vulkan loader will scan the system for well-known Windows +AppX/MSIX packages. +If a package is found, the loader will scan the root directory of this installed +package for JSON manifest files. At this time, the only package that is known is +Microsoft's +[OpenCL™, OpenGL®, and Vulkan® Compatibility Pack](https://apps.microsoft.com/store/detail/9NQPSL29BFFF?hl=en-us&gl=US). + The Vulkan loader will open each manifest file to obtain information about the layer, including the name or pathname of a shared library (".dll") file. @@ -249,8 +256,18 @@ of search folders and will therefore be searched first. If `VK_LAYER_PATH` is present, then `VK_ADD_LAYER_PATH` will not be used by the loader and any values will be ignored. -For security reasons, both `VK_LAYER_PATH` and `VK_ADD_LAYER_PATH` are ignored -if running with elevated privileges. +If `VK_IMPLICIT_LAYER_PATH` is defined, then the loader will look at the paths +defined by that variable for implicit layer manifest files instead of using the +information provided by the implicit layer registry keys. + +If `VK_ADD_IMPLICIT_LAYER_PATH` is defined, then the loader will look at the provided +paths for implicit layer manifest files in addition to using the information +provided by the implicit layer registry keys. +The paths provided by `VK_ADD_IMPLICIT_LAYER_PATH` are added before the standard list +of search folders and will therefore be searched first. + +For security reasons, `VK_LAYER_PATH`, `VK_ADD_LAYER_PATH`, `VK_IMPLICIT_LAYER_PATH`, +and `VK_ADD_IMPLICIT_LAYER_PATH` are ignored if running with elevated privileges. See [Exception for Elevated Privileges](#exception-for-elevated-privileges) for more info. @@ -346,8 +363,21 @@ of search folders and will therefore be searched first. If `VK_LAYER_PATH` is present, then `VK_ADD_LAYER_PATH` will not be used by the loader and any values will be ignored. -For security reasons, both `VK_LAYER_PATH` and `VK_ADD_LAYER_PATH` are ignored -if running with elevated privileges. +If `VK_IMPLICIT_LAYER_PATH` is defined, then the loader will look at the paths +defined by that variable for implicit layer manifest files instead of using the +information provided by the standard implicit layer paths mentioned above. + +If `VK_ADD_IMPLICIT_LAYER_PATH` is defined, then the loader will look at the +provided paths for implicit layer manifest files in addition to using the +information provided by the standard implicit layer paths mentioned above. +The paths provided by `VK_ADD_IMPLICIT_LAYER_PATH` are added before the standard +list of search folders and will therefore be searched first. + +If `VK_IMPLICIT_LAYER_PATH` is present, then `VK_ADD_IMPLICIT_LAYER_PATH` will +not be used by the loader and any values will be ignored. + +For security reasons, `VK_LAYER_PATH`, `VK_ADD_LAYER_PATH`, `VK_IMPLICIT_LAYER_PATH`, +and `VK_ADD_IMPLICIT_LAYER_PATH` are ignored if running with elevated privileges. See [Exception for Elevated Privileges](#exception-for-elevated-privileges) for more info. @@ -360,10 +390,10 @@ See in the [LoaderApplicationInterface.md document](LoaderApplicationInterface.md) for more information on this. -It is also important to note that while both `VK_LAYER_PATH` and -`VK_ADD_LAYER_PATH` will point the loader paths to search for finding the -manifest files, it does not guarantee the library files mentioned by the -manifest will immediately be found. +It is also important to note that while `VK_LAYER_PATH`, `VK_ADD_LAYER_PATH`, +`VK_IMPLICIT_LAYER_PATH`, and `VK_ADD_IMPLICIT_LAYER_PATH` will point the +loader at paths to search for finding the manifest files, it does not guarantee +the library files mentioned by the manifest will immediately be found. Often, the layer manifest file will point to the library file using a relative or absolute path. When a relative or absolute path is used, the loader can typically find the @@ -431,8 +461,8 @@ following: The loader supports filter environment variables which can forcibly enable and disable known layers. Known layers are those that are already found by the loader taking into account -default search paths and other environment variables -(like `VK_LAYER_PATH` or `VK_ADD_LAYER_PATH`). +default search paths and environment variables `VK_LAYER_PATH`, `VK_ADD_LAYER_PATH`, +`VK_IMPLICIT_LAYER_PATH`, and `VK_ADD_IMPLICIT_LAYER_PATH`. The filter variables will be compared against the layer name provided in the layer's manifest file. @@ -456,7 +486,7 @@ will show for each layer that has been forced on. This message will look like the following: ``` -WARNING | LAYER: Layer "VK_LAYER_LUNARG_wrap_objects" force enabled due to env var 'VK_LOADER_LAYERS_ENABLE' +[Vulkan Loader] WARNING | LAYER: Layer "VK_LAYER_LUNARG_wrap_objects" force enabled due to env var 'VK_LOADER_LAYERS_ENABLE' ``` #### Layer Disable Filtering @@ -478,7 +508,7 @@ will show for each layer that has been forcibly disabled. This message will look like the following: ``` -WARNING | LAYER: Layer "VK_LAYER_LUNARG_wrap_objects" disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE' +[Vulkan Loader] WARNING | LAYER: Layer "VK_LAYER_LUNARG_wrap_objects" disabled because name matches filter of env var 'VK_LOADER_LAYERS_DISABLE' ``` #### Layer Special Case Disable @@ -541,8 +571,9 @@ override any disables supplied in `VK_LOADER_LAYERS_DISABLE`. ### Exception for Elevated Privileges -For security reasons, `VK_LAYER_PATH` and `VK_ADD_LAYER_PATH` are ignored if -running the Vulkan application with elevated privileges. +For security reasons, `VK_LAYER_PATH`, `VK_ADD_LAYER_PATH`, `VK_IMPLICIT_LAYER_PATH` +and `VK_ADD_IMPLICIT_LAYER_PATH` are ignored if running the Vulkan application +with elevated privileges. This is because they may insert new libraries into the executable process that are not normally found by the loader. Because of this, these environment variables can only be used for applications @@ -979,7 +1010,15 @@ ignore `instance` when `pName` is `vkCreateDevice`. - The specification **requires** `NULL` to be returned from `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` for disabled functions. - A layer may return `NULL` itself or rely on the following layers to do so. - + - A layer's implementation `vkGetInstanceProcAddr` **should**, when querying for +`vkCreateInstance`, return a valid function pointer regardless of the value of the +`instance` parameter. + - The specification **requires** that the `instance` parameter **must** be NULL. +However, older versions of the specification did not have this requirement, allowing +for non-NULL `instance` handles to be passed in and return a valid `vkCreateInstance` +function pointer. The Vulkan-Loader itself does this and will continue to do so to +maintain compatibility with layers which were released before this specification +change was made. ## Layer Dispatch Initialization @@ -2681,9 +2720,9 @@ Android Vulkan documentation. LLP_LOADER_13 A loader must not load from user-defined paths (including the - use of either VK_LAYER_PATH or VK_ADD_LAYER_PATH - environment variables) when running elevated (Administrator/Super-user) - applications.
+ use of VK_LAYER_PATH, VK_ADD_LAYER_PATH, VK_IMPLICIT_LAYER_PATH, + or VK_ADD_IMPLICIT_LAYER_PATH environment variables) when running + elevated (Administrator/Super-user) applications.
This is for security reasons. The behavior is undefined and may result in computer security lapses, diff --git a/docs/images/svgs/function_device_chain.svg b/docs/images/svgs/function_device_chain.svg old mode 100755 new mode 100644 diff --git a/docs/images/svgs/function_instance_chain.svg b/docs/images/svgs/function_instance_chain.svg old mode 100755 new mode 100644 diff --git a/docs/images/svgs/high_level_loader.svg b/docs/images/svgs/high_level_loader.svg old mode 100755 new mode 100644 diff --git a/docs/images/svgs/loader_device_chain_app.svg b/docs/images/svgs/loader_device_chain_app.svg old mode 100755 new mode 100644 diff --git a/docs/images/svgs/loader_device_chain_loader.svg b/docs/images/svgs/loader_device_chain_loader.svg old mode 100755 new mode 100644 diff --git a/docs/images/svgs/loader_instance_chain.svg b/docs/images/svgs/loader_instance_chain.svg old mode 100755 new mode 100644 diff --git a/docs/images/svgs/loader_layer_order.svg b/docs/images/svgs/loader_layer_order.svg old mode 100755 new mode 100644 diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 622937b2a46300d19507cac7b75cb9a84149336f..624192dc3741b8e483079d02f70a9423cd3f37d9 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -31,18 +31,6 @@ if(WIN32) set(CMAKE_C_STANDARD_LIBRARIES " ") # space is intentional endif() - # ~~~ - # Build dev_ext_trampoline.c and unknown_ext_chain.c with /O2 to allow tail-call optimization. - # Setup two CMake targets (loader-norm and loader-opt) for the different compilation flags. - # ~~~ - set(MODIFIED_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) - - string(REPLACE "/Od" "/O2" MODIFIED_C_FLAGS_DEBUG ${MODIFIED_C_FLAGS_DEBUG}) - string(REPLACE "/Ob0" "/Ob2" MODIFIED_C_FLAGS_DEBUG ${MODIFIED_C_FLAGS_DEBUG}) - string(REGEX REPLACE "/RTC." "" MODIFIED_C_FLAGS_DEBUG ${MODIFIED_C_FLAGS_DEBUG}) #remove run-time error checks - - separate_arguments(MODIFIED_C_FLAGS_DEBUG WINDOWS_COMMAND ${MODIFIED_C_FLAGS_DEBUG}) - # ~~~ # Only generate the loader.rc file with CMake if BUILD_DLL_VERSIONINFO was set. # This feature is for the Vulkan Runtime build @@ -72,6 +60,9 @@ else() if(HAVE_ALLOCA_H) target_compile_definitions(loader_specific_options INTERFACE HAVE_ALLOCA_H) endif() + + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) endif() set(NORMAL_LOADER_SRCS @@ -91,6 +82,8 @@ set(NORMAL_LOADER_SRCS loader.h log.c log.h + loader_json.c + loader_json.h settings.c settings.h terminator.c @@ -118,15 +111,13 @@ endif() set(OPT_LOADER_SRCS dev_ext_trampoline.c phys_dev_ext.c) +set(ASM_FAILURE_MSG "Support for unknown physical device and device functions is disabled due to missing the required assembly support code. \ +To support unknown functions, assembly must be added for the platform.\n") +set(ARMASM_CMAKE_FAILURE_MSG "Support for unknown physical device and device functions is disabled due to the CMake version ${CMAKE_VERSION} \ +being older than 3.26. Please update CMake to version 3.26 or newer.\n") + # Check for assembler support -set(ASM_FAILURE_MSG "The build will fall back on building with C code\n") -set(ASM_FAILURE_MSG "${ASM_FAILURE_MSG}Note that this may be unsafe, as the C code requires tail-call optimizations to remove") -set(ASM_FAILURE_MSG "${ASM_FAILURE_MSG} the stack frame for certain calls. If the compiler does not do this, then unknown device") -set(ASM_FAILURE_MSG "${ASM_FAILURE_MSG} extensions will suffer from a corrupted stack.") - -if (APPLE_UNIVERSAL_BINARY) - set(USE_ASSEMBLY_FALLBACK ON) -elseif(WIN32) +if(WIN32 AND NOT USE_GAS) option(USE_MASM "Use MASM" ON) if(USE_MASM AND MINGW) find_program(JWASM_FOUND NAMES jwasm uasm) @@ -144,12 +135,42 @@ elseif(WIN32) endif() endif() if (USE_MASM) - enable_language(ASM_MASM) + if(SYSTEM_PROCESSOR MATCHES "arm") + if(CMAKE_VERSION VERSION_LESS "3.26.0") + set(ASM_FAILURE_MSG ${ARMASM_CMAKE_FAILURE_MSG}) + else() + enable_language(ASM_MARMASM) + set(LOADER_ASM_DIALECT "MARMASM") + endif() + else() + enable_language(ASM_MASM) + set(LOADER_ASM_DIALECT "MASM") + endif() endif() + # Test if the detected compiler actually works. # Unfortunately, CMake's detection of ASM_MASM is not reliable, so we need to do this ourselves. - if (CMAKE_SIZEOF_VOID_P EQUAL 4) - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[ + if(SYSTEM_PROCESSOR MATCHES "arm") + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[ +test_func FUNCTION + mov r0, #0 + bx lr + ENDFUNC + END +]=]) + else() + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[ +test_func FUNCTION + mov x1, 26 + ldr x0, [x0, x1] + ENDFUNC + END +]=]) + endif() + else() + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[ .model flat .code extrn _start:near @@ -157,36 +178,48 @@ extrn _start:near ret end ]=]) - else() - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[ + else() + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm [=[ .code extrn start:near xor rax, rax ret end ]=]) + endif() endif () + if(MINGW) set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} ${JWASM_FLAGS}) - elseif(NOT CMAKE_CL_64 AND NOT JWASM_FOUND) - set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} /safeseh) + set(CMAKE_ASM_MARMASM_FLAGS ${CMAKE_ASM_MARMASM_FLAGS} ${JWASM_FLAGS}) + elseif(NOT CMAKE_CL_64 AND NOT JWASM_FOUND AND CMAKE_SIZEOF_VOID_P EQUAL 4) + set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} /safeseh) # /safeseh is only needed in x86 endif() + # try_compile does not work here due to the same reasons as static above. - execute_process(COMMAND ${CMAKE_ASM_MASM_COMPILER} ${CMAKE_ASM_MASM_FLAGS} -c -Fo ${CMAKE_CURRENT_BINARY_DIR}/masm_check.obj ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm - RESULT_VARIABLE CMAKE_ASM_MASM_COMPILER_WORKS - OUTPUT_QUIET ERROR_QUIET) + if(SYSTEM_PROCESSOR MATCHES "arm") + execute_process(COMMAND ${CMAKE_ASM_MARMASM_COMPILER} ${CMAKE_ASM_MARMASM_FLAGS} ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm + RESULT_VARIABLE ASM_COMPILER_WORKS + OUTPUT_QUIET ERROR_QUIET) + else() + execute_process(COMMAND ${CMAKE_ASM_MASM_COMPILER} ${CMAKE_ASM_MASM_FLAGS} -c -Fo ${CMAKE_CURRENT_BINARY_DIR}/masm_check.obj ${CMAKE_CURRENT_BINARY_DIR}/masm_check.asm + RESULT_VARIABLE ASM_COMPILER_WORKS + OUTPUT_QUIET ERROR_QUIET) + endif() + # Convert the return code to a boolean - if(CMAKE_ASM_MASM_COMPILER_WORKS EQUAL 0) - set(CMAKE_ASM_MASM_COMPILER_WORKS true) + if(ASM_COMPILER_WORKS EQUAL 0) + set(ASM_COMPILER_WORKS true) else() - set(CMAKE_ASM_MASM_COMPILER_WORKS false) + set(ASM_COMPILER_WORKS false) endif() - if(CMAKE_ASM_MASM_COMPILER_WORKS) + + if(ASM_COMPILER_WORKS) add_executable(asm_offset asm_offset.c) target_link_libraries(asm_offset PRIVATE loader_specific_options) # If am emulator is provided (Like Wine), or running on native, run asm_offset to generate gen_defines.asm if (CMAKE_CROSSCOMPILING_EMULATOR OR NOT CMAKE_CROSSCOMPILING) - add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset MASM) + add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset ${LOADER_ASM_DIALECT}) else() # Forces compiler to write the intermediate asm file, needed so that we can get sizeof/offset of info out of it. target_compile_options(asm_offset PRIVATE "/Fa$/asm_offset.asm" /FA) @@ -197,22 +230,23 @@ end # Run parse_asm_values.py on asm_offset's assembly file to generate the gen_defines.asm, which the asm code depends on add_custom_command(TARGET asm_offset POST_BUILD COMMAND Python3::Interpreter ${PROJECT_SOURCE_DIR}/scripts/parse_asm_values.py "${CMAKE_CURRENT_BINARY_DIR}/gen_defines.asm" - "$/asm_offset.asm" "MASM" "${CMAKE_C_COMPILER_ID}" "${CMAKE_SYSTEM_PROCESSOR}" + "$/asm_offset.asm" "${LOADER_ASM_DIALECT}" "${CMAKE_C_COMPILER_ID}" "${SYSTEM_PROCESSOR}" BYPRODUCTS gen_defines.asm ) endif() add_custom_target(loader_asm_gen_files DEPENDS gen_defines.asm) set_target_properties(loader_asm_gen_files PROPERTIES FOLDER ${LOADER_HELPER_FOLDER}) - add_library(loader-unknown-chain OBJECT unknown_ext_chain_masm.asm) - target_link_libraries(loader-unknown-chain Vulkan::Headers) - target_include_directories(loader-unknown-chain PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) - add_dependencies(loader-unknown-chain loader_asm_gen_files) + if(SYSTEM_PROCESSOR MATCHES "arm") + list(APPEND OPT_LOADER_SRCS unknown_ext_chain_marmasm.asm) + else() + list(APPEND OPT_LOADER_SRCS unknown_ext_chain_masm.asm) + endif() + set(UNKNOWN_FUNCTIONS_SUPPORTED ON) else() - set(USE_ASSEMBLY_FALLBACK ON) - message(WARNING "Could not find working MASM assembler\n${ASM_FAILURE_MSG}") + message(WARNING "Could not find working ${} assembler\n${ASM_FAILURE_MSG}") endif() -elseif(UNIX) # i.e.: Linux & Apple +elseif(UNIX OR MINGW OR (WIN32 AND USE_GAS)) # i.e.: Linux & Apple & MinGW & Windows using Clang-CL option(USE_GAS "Use GAS" ON) if(USE_GAS) @@ -225,13 +259,31 @@ elseif(UNIX) # i.e.: Linux & Apple set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}") set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64|arm64") - try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch64.S) + if(WIN32 AND ${SYSTEM_PROCESSOR} MATCHES "arm64") + # In this particular case, we are using the same approach as for NASM + # This specifically avoids CMake issue #18889 + execute_process(COMMAND ${CMAKE_ASM_COMPILER} ${CMAKE_ASM_FLAGS} -c -o ${CMAKE_CURRENT_BINARY_DIR}/asm_test_aarch64.obj ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch64.S + RESULT_VARIABLE WIN_ARM64_ASSEMBLER_WORKS + OUTPUT_QUIET ERROR_QUIET) + if(WIN_ARM64_ASSEMBLER_WORKS EQUAL 0) + set(ASSEMBLER_WORKS true) + endif() + + if(ASSEMBLER_WORKS) + list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_aarch.S) + endif() + elseif (${SYSTEM_PROCESSOR} MATCHES "aarch64|arm64") + try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch64.S OUTPUT_VARIABLE TRY_COMPILE_OUTPUT) + if(ASSEMBLER_WORKS) + list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_aarch.S) + endif() + elseif (${SYSTEM_PROCESSOR} MATCHES "aarch32|armhf|armv7l|armv8l") + try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_aarch32.S OUTPUT_VARIABLE TRY_COMPILE_OUTPUT) if(ASSEMBLER_WORKS) - set(OPT_LOADER_SRCS ${OPT_LOADER_SRCS} unknown_ext_chain_gas_aarch64.S) + list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_aarch.S) endif() # Covers x86_64, amd64, x86, i386, i686, I386, I686 - elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "amd64|86") + elseif(${SYSTEM_PROCESSOR} MATCHES "amd64|86") check_include_file("cet.h" HAVE_CET_H) if(HAVE_CET_H) target_compile_definitions(loader_specific_options INTERFACE HAVE_CET_H) @@ -239,18 +291,22 @@ elseif(UNIX) # i.e.: Linux & Apple try_compile(ASSEMBLER_WORKS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/asm_test_x86.S) if(ASSEMBLER_WORKS) - set(OPT_LOADER_SRCS ${OPT_LOADER_SRCS} unknown_ext_chain_gas_x86.S) + list(APPEND OPT_LOADER_SRCS unknown_ext_chain_gas_x86.S) endif() endif() endif() # When compiling for x86 on x64, we can't use CMAKE_SYSTEM_PROCESSOR to determine which architecture to use, - # Instead, check the size of void* and if its 4, set ASM_OFFSET_SYSTEM_PROCESSOR to x86 - # Note - there is no 32 bit arm assembly code, so this only applies to x86 currently. + # Instead, check the size of void* and if its 4, set ASM_OFFSET_SYSTEM_PROCESSOR to x86 if we aren't on arm if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(ASM_OFFSET_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) # x86_64 or aarch64/arm64 + set(ASM_OFFSET_SYSTEM_PROCESSOR ${SYSTEM_PROCESSOR}) # x86_64 or aarch64/arm64 + string(REPLACE amd64 x86_64 ASM_OFFSET_SYSTEM_PROCESSOR "${ASM_OFFSET_SYSTEM_PROCESSOR}") else() - set(ASM_OFFSET_SYSTEM_PROCESSOR "x86") + if(${SYSTEM_PROCESSOR} MATCHES "86") + set(ASM_OFFSET_SYSTEM_PROCESSOR "x86") + else() + set(ASM_OFFSET_SYSTEM_PROCESSOR ${SYSTEM_PROCESSOR}) + endif() endif() if(ASSEMBLER_WORKS) @@ -261,7 +317,14 @@ elseif(UNIX) # i.e.: Linux & Apple add_custom_command(OUTPUT gen_defines.asm DEPENDS asm_offset COMMAND asm_offset GAS) else() # Forces compiler to write the intermediate asm file, needed so that we can get sizeof/offset of info out of it. - target_compile_options(asm_offset PRIVATE -save-temps=obj) + # If with lto, compiler will output IR instead of asm, so we need to explicitly disable lto here. + if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_compile_options(asm_offset PRIVATE -save-temps=obj -fno-lto) + elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + target_compile_options(asm_offset PRIVATE -save-temps=obj -fno-lto -fno-whole-program-vtables -fno-virtual-function-elimination) + else() + target_compile_options(asm_offset PRIVATE -save-temps=obj) + endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") set(ASM_OFFSET_EXECUTABLE_LOCATION "$/gen_defines.asm") set(ASM_OFFSET_INTERMEDIATE_LOCATION "$/CMakeFiles/asm_offset.dir/asm_offset.c.s") @@ -275,6 +338,7 @@ elseif(UNIX) # i.e.: Linux & Apple else() message(FATAL_ERROR "C_COMPILER_ID not supported!") endif() + message(STATUS "CMAKE_CROSSCOMPILING FALSE") find_package(Python3 REQUIRED QUIET) # Run parse_asm_values.py on asm_offset's assembly file to generate the gen_defines.asm, which the asm code depends on @@ -289,8 +353,8 @@ elseif(UNIX) # i.e.: Linux & Apple if (APPLE) set(MODIFY_UNKNOWN_FUNCTION_DECLS ON) endif() + set(UNKNOWN_FUNCTIONS_SUPPORTED ON) else() - set(USE_ASSEMBLY_FALLBACK ON) if(USE_GAS) message(WARNING "Could not find working ${ASM_OFFSET_SYSTEM_PROCESSOR} GAS assembler\n${ASM_FAILURE_MSG}") else() @@ -299,40 +363,24 @@ elseif(UNIX) # i.e.: Linux & Apple endif() endif() -if(USE_ASSEMBLY_FALLBACK) - add_custom_target(loader_asm_gen_files) - if (MSVC) - add_library(loader-unknown-chain OBJECT unknown_ext_chain.c) - target_link_libraries(loader-unknown-chain loader_specific_options) - set_target_properties(loader-unknown-chain PROPERTIES CMAKE_C_FLAGS_DEBUG "${MODIFIED_C_FLAGS_DEBUG}") - else() - set(OPT_LOADER_SRCS ${OPT_LOADER_SRCS} unknown_ext_chain.c) - set_source_files_properties(${OPT_LOADER_SRCS} PROPERTIES COMPILE_FLAGS -O) - endif() +if(UNKNOWN_FUNCTIONS_SUPPORTED) + list(APPEND NORMAL_LOADER_SRCS ${OPT_LOADER_SRCS}) endif() if(WIN32) - add_library(loader-opt STATIC ${OPT_LOADER_SRCS}) - target_link_libraries(loader-opt PUBLIC loader_specific_options) - add_dependencies(loader-opt loader_asm_gen_files) - set_target_properties(loader-opt PROPERTIES CMAKE_C_FLAGS_DEBUG "${MODIFIED_C_FLAGS_DEBUG}") - # If BUILD_DLL_VERSIONINFO was set, use the loader.rc in the build dir, otherwise use the checked in file set(RC_FILE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/loader.rc) if (NOT "$CACHE{BUILD_DLL_VERSIONINFO}" STREQUAL "") set(RC_FILE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/loader.rc) endif() - set(LOADER_UNKNOWN_CHAIN_LIBRARY $<$:$>) - add_library(vulkan SHARED ${NORMAL_LOADER_SRCS} - ${LOADER_UNKNOWN_CHAIN_LIBRARY} ${CMAKE_CURRENT_SOURCE_DIR}/${API_TYPE}-1.def ${RC_FILE_LOCATION}) - target_link_libraries(vulkan PRIVATE loader_specific_options loader-opt) + target_link_libraries(vulkan PRIVATE loader_specific_options) # when adding the suffix the import and runtime library names must be consistent # mingw: libvulkan-1.dll.a / vulkan-1.dll @@ -354,8 +402,6 @@ if(WIN32) target_link_libraries(vulkan PRIVATE cfgmgr32) endif() - add_dependencies(vulkan loader_asm_gen_files) - else() if(APPLE) option(APPLE_STATIC_LOADER "Build a loader that can be statically linked. Intended for Chromium usage/testing.") @@ -372,18 +418,13 @@ else() add_library(vulkan SHARED) endif() - target_sources(vulkan PRIVATE ${NORMAL_LOADER_SRCS} ${OPT_LOADER_SRCS}) - - add_dependencies(vulkan loader_asm_gen_files) + target_sources(vulkan PRIVATE ${NORMAL_LOADER_SRCS}) set_target_properties(vulkan PROPERTIES SOVERSION "1" VERSION "${VULKAN_LOADER_VERSION}" ) - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) - target_link_libraries(vulkan PRIVATE ${CMAKE_DL_LIBS} m Threads::Threads) set_target_properties(vulkan PROPERTIES OUTPUT_NAME ${API_TYPE}) @@ -412,9 +453,12 @@ else() file(GLOB_RECURSE CONFIGURE_DEPENDS FRAMEWORK_HEADERS ${VulkanHeaders_INCLUDE_DIRS}) add_library(vulkan-framework SHARED) - target_sources(vulkan-framework PRIVATE ${NORMAL_LOADER_SRCS} ${OPT_LOADER_SRCS} ${FRAMEWORK_HEADERS}) + target_sources(vulkan-framework PRIVATE ${NORMAL_LOADER_SRCS} ${FRAMEWORK_HEADERS}) + + if (UNKNOWN_FUNCTIONS_SUPPORTED) + add_dependencies(vulkan-framework loader_asm_gen_files) + endif() - add_dependencies(vulkan-framework loader_asm_gen_files) target_link_libraries(vulkan-framework ${CMAKE_DL_LIBS} Threads::Threads -lm "-framework CoreFoundation") target_link_libraries(vulkan-framework loader_specific_options) @@ -435,6 +479,8 @@ else() FRAMEWORK TRUE FRAMEWORK_VERSION A VERSION "${VULKAN_LOADER_VERSION}" + MACOSX_FRAMEWORK_BUNDLE_VERSION "${VULKAN_LOADER_VERSION}" + MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${VULKAN_LOADER_VERSION}" SOVERSION "1.0.0" MACOSX_FRAMEWORK_IDENTIFIER com.lunarg.vulkanFramework PUBLIC_HEADER "${FRAMEWORK_HEADERS}" @@ -466,6 +512,15 @@ target_link_libraries(vulkan PRIVATE loader_specific_options) target_link_libraries(vulkan PRIVATE Vulkan::Headers) add_library(Vulkan::Loader ALIAS vulkan) +if (UNKNOWN_FUNCTIONS_SUPPORTED) + target_compile_definitions(vulkan PRIVATE UNKNOWN_FUNCTIONS_SUPPORTED) + add_dependencies(vulkan loader_asm_gen_files) +endif() + +if (BUILD_TESTS) + target_compile_definitions(vulkan PRIVATE SHOULD_EXPORT_TEST_FUNCTIONS) +endif() + if (APPLE_STATIC_LOADER) # TLDR: This feature only exists at the request of Google for Chromium. No other project should use this! message(NOTICE "Apple STATIC lib: it will be built but not installed, and vulkan.pc and VulkanLoaderConfig.cmake won't be generated!") @@ -511,3 +566,7 @@ if (PKG_CONFIG_FOUND) configure_file("vulkan.pc.in" "vulkan.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/vulkan.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" RENAME "${API_TYPE}.pc") endif() + +if (CODE_COVERAGE) + target_code_coverage(vulkan AUTO ALL) +endif() diff --git a/loader/LoaderAndLayerInterface.md b/loader/LoaderAndLayerInterface.md index 41ddf786aab53ddfadf124c5c1ba739fcfea40fb..3a3249cca455e8f24cffa653a84ff37aebfea791 100644 --- a/loader/LoaderAndLayerInterface.md +++ b/loader/LoaderAndLayerInterface.md @@ -15,5 +15,5 @@ This document has been renamed to [LoaderInterfaceArchitecture.md](../docs/LoaderInterfaceArchitecture.md) and can be -found in the top-level docs folder of the Loader GitHb repo. +found in the top-level docs folder of the Loader GitHub repo. Please refer to that document going forward. diff --git a/loader/allocation.c b/loader/allocation.c index 3397ef992f4e7a16449bc0510087b9ae8eb64d42..4ad68027e55421e9e2811dd7ba22b5b802b2345e 100644 --- a/loader/allocation.c +++ b/loader/allocation.c @@ -98,6 +98,10 @@ void *loader_realloc(const VkAllocationCallbacks *pAllocator, void *pMemory, siz #endif } else { pNewMem = realloc(pMemory, size); + // Clear out the newly allocated memory + if (size > orig_size) { + memset((uint8_t *)pNewMem + orig_size, 0, size - orig_size); + } } return pNewMem; } diff --git a/loader/allocation.h b/loader/allocation.h index 6e2d7ef23bd4ef0579fa6aeb89f9e61b7b3331ef..e9c5bc59ac45cc1b57e5d8d6fd6730f2e2378daa 100644 --- a/loader/allocation.h +++ b/loader/allocation.h @@ -29,7 +29,6 @@ #pragma once #include "loader_common.h" -#include "stack_allocation.h" void *loader_instance_heap_alloc(const struct loader_instance *instance, size_t size, VkSystemAllocationScope allocation_scope); void *loader_instance_heap_calloc(const struct loader_instance *instance, size_t size, VkSystemAllocationScope allocation_scope); diff --git a/loader/asm_offset.c b/loader/asm_offset.c index f99ff56a0453d2c7d14fc80dc205e20f2063fe71..4d01f8bfc58aa9c8babfdfafe7116f13b5e4375d 100644 --- a/loader/asm_offset.c +++ b/loader/asm_offset.c @@ -1,7 +1,7 @@ /* - * Copyright (c) 2017-2021 The Khronos Group Inc. - * Copyright (c) 2017-2021 Valve Corporation - * Copyright (c) 2017-2021 LunarG, Inc. + * Copyright (c) 2017-2024 The Khronos Group Inc. + * Copyright (c) 2017-2024 Valve Corporation + * Copyright (c) 2017-2024 LunarG, Inc. * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -78,19 +78,28 @@ struct ValueInfo { const char *comment; }; +enum Assembler { + UNKNOWN = 0, + MASM = 1, + MARMASM = 2, + GAS = 3, +}; + // This file can both be executed to produce gen_defines.asm and contains all the relevant data which // the parse_asm_values.py script needs to write gen_defines.asm, necessary for cross compilation int main(int argc, char **argv) { - const char *assembler = NULL; + enum Assembler assembler = UNKNOWN; for (int i = 0; i < argc; ++i) { if (!strcmp(argv[i], "MASM")) { - assembler = "MASM"; + assembler = MASM; + } else if (!strcmp(argv[i], "MARMASM")) { + assembler = MARMASM; } else if (!strcmp(argv[i], "GAS")) { - assembler = "GAS"; + assembler = GAS; } } - if (assembler == NULL) { - return 1; + if (assembler == UNKNOWN) { + return -1; } struct ValueInfo values[] = { @@ -98,7 +107,7 @@ int main(int argc, char **argv) { { .name = "VULKAN_LOADER_ERROR_BIT", .value = (size_t) VULKAN_LOADER_ERROR_BIT, .comment = "The numerical value of the enum value 'VULKAN_LOADER_ERROR_BIT'" }, { .name = "PTR_SIZE", .value = sizeof(void*), - .comment = "The size of a pointer" }, + .comment = "The size of a pointer" }, { .name = "CHAR_PTR_SIZE", .value = sizeof(char *), .comment = "The size of a 'const char *' struct" }, { .name = "FUNCTION_OFFSET_INSTANCE", .value = offsetof(struct loader_instance, phys_dev_ext_disp_functions), @@ -122,19 +131,34 @@ int main(int argc, char **argv) { FILE *file = loader_fopen("gen_defines.asm", "w"); fprintf(file, "\n"); - if (!strcmp(assembler, "MASM")) { + if (assembler == MASM) { for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { fprintf(file, "%-32s equ " SIZE_T_FMT "; %s\n", values[i].name, values[i].value, values[i].comment); } - } else if (!strcmp(assembler, "GAS")) { + } else if (assembler == MARMASM) { + fprintf(file, " AREA loader_structs_details, DATA,READONLY\n"); +#if defined(__aarch64__) || defined(_M_ARM64) + fprintf(file, "AARCH_64 EQU 1\n"); +#else + fprintf(file, "AARCH_64 EQU 0\n"); +#endif + for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { + fprintf(file, "%-32s EQU " SIZE_T_FMT "; %s\n", values[i].name, values[i].value, values[i].comment); + } + fprintf(file, " END\n"); + } else if (assembler == GAS) { #if defined(__x86_64__) || defined(__i386__) const char *comment_delimiter = "#"; #if defined(__x86_64__) fprintf(file, ".set X86_64, 1\n"); #endif // defined(__x86_64__) -#elif defined(__aarch64__) +#elif defined(__aarch64__) || defined(__arm__) const char *comment_delimiter = "//"; +#if defined(__aarch64__) fprintf(file, ".set AARCH_64, 1\n"); +#else + fprintf(file, ".set AARCH_64, 0\n"); +#endif #else // Default comment delimiter const char *comment_delimiter = "#"; diff --git a/loader/asm_test_aarch32.S b/loader/asm_test_aarch32.S new file mode 100644 index 0000000000000000000000000000000000000000..434abf275f1e2f6def3ff81e24bc710f1b773822 --- /dev/null +++ b/loader/asm_test_aarch32.S @@ -0,0 +1,25 @@ +// +// Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2024 Valve Corporation +// Copyright (c) 2024 LunarG, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +.text +.global sample +.set PHYS_DEV_OFFSET_INST_DISPATCH, 10 +.set PTR_SIZE, 4 +sample: + mov r1, #(PHYS_DEV_OFFSET_INST_DISPATCH + (PTR_SIZE * 4)) + ldr r0, [r0, r1] diff --git a/loader/cJSON.c b/loader/cJSON.c index b07d9518a1909701393b33c597e55a84d011944f..d4eb0c93da0dd25c039911a2531cca2263f99b80 100644 --- a/loader/cJSON.c +++ b/loader/cJSON.c @@ -1,8 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble - Copyright (c) 2015-2021 The Khronos Group Inc. - Copyright (c) 2015-2021 Valve Corporation - Copyright (c) 2015-2021 LunarG, Inc. + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,1088 +23,1784 @@ /* cJSON */ /* JSON parser in C. */ -#include -#include -#include -#include +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning(push) +/* disable warning about single line comments in system headers */ +#pragma warning(disable : 4001) +#endif + +#include #include +#include #include -#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif #include "cJSON.h" +#include + #include "allocation.h" -#include "loader.h" -#include "log.h" -static void *cJSON_malloc(const VkAllocationCallbacks *pAllocator, size_t size) { - return loader_calloc(pAllocator, size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0 / 0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = {NULL, 0}; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { return (const char *)(global_error.json + global_error.position); } + +CJSON_PUBLIC(char *) loader_cJSON_GetStringValue(const cJSON *const item) { + if (!loader_cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; } -static void *cJSON_malloc_instance_scope(const VkAllocationCallbacks *pAllocator, size_t size) { - return loader_calloc(pAllocator, size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); +CJSON_PUBLIC(double) loader_cJSON_GetNumberValue(const cJSON *const item) { + if (!loader_cJSON_IsNumber(item)) { + return (double)NAN; + } + + return item->valuedouble; } -static void cJSON_Free(const VkAllocationCallbacks *pAllocator, void *pMemory) { loader_free(pAllocator, pMemory); } +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif -/* -// commented out as it is unused - static error code channel requires external locks to be used. -static const char *ep; +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { + if ((string1 == NULL) || (string2 == NULL)) { + return 1; + } -const char *cJSON_GetErrorPtr(void) { return ep; } -*/ + if (string1 == string2) { + return 0; + } -static char *cJSON_strdup(const VkAllocationCallbacks *pAllocator, const char *str) { - size_t len; - char *copy; + for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if (*string1 == '\0') { + return 0; + } + } - len = strlen(str) + 1; - copy = (char *)cJSON_malloc(pAllocator, len); - if (!copy) return 0; - memcpy(copy, str, len); - return copy; + return tolower(*string1) - tolower(*string2); } +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + /* Internal constructor. */ static cJSON *cJSON_New_Item(const VkAllocationCallbacks *pAllocator) { - cJSON *node = (cJSON *)cJSON_malloc(pAllocator, sizeof(cJSON)); - if (node) { - memset(node, 0, sizeof(cJSON)); - node->pAllocator = (VkAllocationCallbacks *)pAllocator; + cJSON *node = (cJSON *)loader_calloc(pAllocator, sizeof(cJSON), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL != node) { + node->pAllocator = pAllocator; } return node; } /* Delete a cJSON structure. */ -void loader_cJSON_Delete(cJSON *c) { - cJSON *next; - while (c) { - next = c->next; - if (!(c->type & cJSON_IsReference) && c->child) loader_cJSON_Delete(c->child); - if (!(c->type & cJSON_IsReference) && c->valuestring) cJSON_Free(c->pAllocator, c->valuestring); - if (!(c->type & cJSON_StringIsConst) && c->string) cJSON_Free(c->pAllocator, c->string); - cJSON_Free(c->pAllocator, c); - c = next; +TEST_FUNCTION_EXPORT CJSON_PUBLIC(void) loader_cJSON_Delete(cJSON *item) { + cJSON *next = NULL; + while (item != NULL) { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { + loader_cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + loader_free(item->pAllocator, item->valuestring); + item->valuestring = NULL; + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + loader_free(item->pAllocator, item->string); + item->string = NULL; + } + loader_free(item->pAllocator, item); + item = next; } } -/* Parse the input text to generate a number, and populate the result into item. - */ -static const char *parse_number(cJSON *item, const char *num) { - double n = 0, sign = 1, scale = 0; - int subscale = 0, signsubscale = 1; - - if (*num == '-') sign = -1, num++; /* Has sign? */ - if (*num == '0') num++; /* is zero */ - if (*num >= '1' && *num <= '9') do - n = (n * 10.0) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ - if (*num == '.' && num[1] >= '0' && num[1] <= '9') { - num++; - do n = (n * 10.0) + (*num++ - '0'), scale--; - while (*num >= '0' && *num <= '9'); - } /* Fractional part? */ - if (*num == 'e' || *num == 'E') /* Exponent? */ - { - num++; - if (*num == '+') - num++; - else if (*num == '-') - signsubscale = -1, num++; /* With sign? */ - while (*num >= '0' && *num <= '9') subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + const VkAllocationCallbacks *pAllocator; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buffer) { + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; } - n = sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- - number.fraction * - 10^+/- exponent */ + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch (buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char *)number_c_string, (char **)&after_end); + if (number_c_string == after_end) { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) { + item->valueint = INT_MAX; + } else if (number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } - item->valuedouble = n; - item->valueint = (int)n; item->type = cJSON_Number; - return num; -} -static size_t pow2gt(size_t x) { - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - return x + 1; + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; } typedef struct { - char *buffer; + unsigned char *buffer; size_t length; size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + const VkAllocationCallbacks *pAllocator; } printbuffer; -static char *ensure(const VkAllocationCallbacks *pAllocator, printbuffer *p, size_t needed) { - char *newbuffer; - size_t newsize; - if (!p || !p->buffer) return 0; - needed += p->offset; - if (needed <= p->length) return p->buffer + p->offset; - - newsize = pow2gt(needed); - newbuffer = (char *)cJSON_malloc(pAllocator, newsize); - if (!newbuffer) { - cJSON_Free(pAllocator, p->buffer); - p->length = 0, p->buffer = 0; - return 0; +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char *ensure(printbuffer *const p, size_t needed, bool *out_of_memory) { + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + newbuffer = (unsigned char *)loader_realloc(p->pAllocator, p->buffer, p->length, newsize, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (newbuffer == NULL) { + *out_of_memory = true; + loader_free(p->pAllocator, p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; } - if (newbuffer) memcpy(newbuffer, p->buffer, p->length); - cJSON_Free(pAllocator, p->buffer); + p->length = newsize; p->buffer = newbuffer; + return newbuffer + p->offset; } -static size_t cJSON_update(printbuffer *p) { - char *str; - if (!p || !p->buffer) return 0; - str = p->buffer + p->offset; - return p->offset + strlen(str); +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer *const buffer) { + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char *)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) { + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); } /* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item, printbuffer *p) { - char *str = 0; - size_t str_buf_size; +static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { + unsigned char *output_pointer = NULL; double d = item->valuedouble; - if (d == 0) { - str_buf_size = 2; /* special case for 0. */ - if (p) - str = ensure(item->pAllocator, p, str_buf_size); - else - str = (char *)cJSON_malloc(item->pAllocator, str_buf_size); - if (str) loader_strncpy(str, str_buf_size, "0", 2); - } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) { - str_buf_size = 21; /* 2^64+1 can be represented in 21 chars. */ - if (p) - str = ensure(item->pAllocator, p, str_buf_size); - else - str = (char *)cJSON_malloc(item->pAllocator, str_buf_size); - if (str) snprintf(str, str_buf_size, "%d", item->valueint); + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) { + length = snprintf((char *)number_buffer, 26, "null"); + } else if (d == (double)item->valueint) { + length = snprintf((char *)number_buffer, 26, "%d", item->valueint); } else { - str_buf_size = 64; /* This is a nice tradeoff. */ - if (p) - str = ensure(item->pAllocator, p, str_buf_size); - else - str = (char *)cJSON_malloc(item->pAllocator, str_buf_size); - if (str) { - if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60) - snprintf(str, str_buf_size, "%.0f", d); - else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) - snprintf(str, str_buf_size, "%e", d); - else - snprintf(str, str_buf_size, "%f", d); + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char *)number_buffer, 26, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char *)number_buffer, 26, "%1.17g", d); + } + } + + /* snprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof(""), out_of_memory); + if (output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) { + if (number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; } + + output_pointer[i] = number_buffer[i]; } - return str; + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; } -static unsigned parse_hex4(const char *str) { - unsigned h = 0; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char *const input) { + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if ((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if ((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if (i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + return h; } -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; -static const char *parse_string(cJSON *item, const char *str, bool *out_of_memory) { - const char *ptr = str + 1; - char *ptr2; - char *out; - int len = 0; - unsigned uc, uc2; - if (*str != '\"') { - // ep = str; // commented out as it is unused - return 0; - } /* not a string! */ +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char *const input_pointer, const unsigned char *const input_end, + unsigned char **output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } - while (*ptr != '\"' && *ptr && ++len) - if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); - out = (char *)cJSON_malloc(item->pAllocator, len + 1); /* This is how long we need for the string, roughly. */ - if (!out) { - *out_of_memory = true; - return 0; + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; } - ptr = str + 1; - ptr2 = out; - while (*ptr != '\"' && *ptr) { - if (*ptr != '\\') - *ptr2++ = *ptr++; + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if (codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if (codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if (codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { + /* is escape sequence */ + if (input_end[0] == '\\') { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char *)loader_calloc(input_buffer->pAllocator, allocation_length + sizeof(""), + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (output == NULL) { + *out_of_memory = true; + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) { + if (*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ else { - ptr++; - switch (*ptr) { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) { + goto fail; + } + + switch (input_pointer[1]) { case 'b': - *ptr2++ = '\b'; + *output_pointer++ = '\b'; break; case 'f': - *ptr2++ = '\f'; + *output_pointer++ = '\f'; break; case 'n': - *ptr2++ = '\n'; + *output_pointer++ = '\n'; break; case 'r': - *ptr2++ = '\r'; + *output_pointer++ = '\r'; break; case 't': - *ptr2++ = '\t'; + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; break; - case 'u': /* transcode utf16 to utf8. */ - uc = parse_hex4(ptr + 1); - ptr += 4; /* get the unicode char. */ - - if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid. */ - - if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate. */ - uc2 = parse_hex4(ptr + 3); - ptr += 6; - if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate. */ - uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); - } - len = 4; - if (uc < 0x80) - len = 1; - else if (uc < 0x800) - len = 2; - else if (uc < 0x10000) - len = 3; - ptr2 += len; - - for (size_t i = len; i > 0; i--) { - if (i == 1) { - *--ptr2 = ((unsigned char)uc | firstByteMark[len]); - } else if (i >= 2) { - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - } + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; } - ptr2 += len; break; + default: - *ptr2++ = *ptr; - break; + goto fail; } - ptr++; + input_pointer += sequence_length; } } - *ptr2 = 0; - if (*ptr == '\"') ptr++; - item->valuestring = out; + + /* zero terminate the output */ + *output_pointer = '\0'; + item->type = cJSON_String; - return ptr; + item->valuestring = (char *)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) { + loader_free(input_buffer->pAllocator, output); + output = NULL; + } + + if (input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const VkAllocationCallbacks *pAllocator, const char *str, printbuffer *p) { - const char *ptr; - char *ptr2; - char *out; - size_t out_buf_size, len = 0, flag = 0; - unsigned char token; - - for (ptr = str; *ptr; ptr++) flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"') || (*ptr == '\\')) ? 1 : 0; - if (!flag) { - len = ptr - str; - out_buf_size = len + 1; - // out_buf_size = len + 3; // Modified to not put quotes around the string - if (p) - out = ensure(pAllocator, p, out_buf_size); - else - out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size); - if (!out) return 0; - ptr2 = out; - // *ptr2++ = '\"'; // Modified to not put quotes around the string - loader_strncpy(ptr2, out_buf_size, str, out_buf_size); - // ptr2[len] = '\"'; // Modified to not put quotes around the string - ptr2[len] = 0; // ptr2[len + 1] = 0; // Modified to not put quotes around the string - return out; - } - - if (!str) { - out_buf_size = 3; - if (p) - out = ensure(pAllocator, p, out_buf_size); - else - out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size); - if (!out) return 0; - loader_strncpy(out, out_buf_size, "\"\"", 3); - return out; - } - ptr = str; - token = *ptr; - while (token && ++len) { - if (strchr("\"\\\b\f\n\r\t", token)) - len++; - else if (token < 32) - len += 5; - ptr++; - token = *ptr; - } - - out_buf_size = len + 1; - // out_buf_size = len + 3; // Modified to not put quotes around the string - if (p) - out = ensure(pAllocator, p, out_buf_size); - else - out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size); - if (!out) return 0; - - ptr2 = out; - ptr = str; - // *ptr2++ = '\"'; // Modified to not put quotes around the string - while (*ptr) { - if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\') - *ptr2++ = *ptr++; - else { - switch (token = *ptr++) { +static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer, bool *out_of_memory) { + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) { + return false; + } + + /* empty string */ + if (input == NULL) { + output = ensure(output_buffer, sizeof(""), out_of_memory); + if (output == NULL) { + return false; + } + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) { + switch (*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof(""), out_of_memory); + if (output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) { + memcpy(output, input, output_length); + output[output_length] = '\0'; + + return true; + } + + output_pointer = output; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + // Loader specific modification - don't add a backslash because that will 'double up' any existing back slashes. + // This change was added right after vulkan's public release, so while it may not be a problem, there are plenty + // of API calls made which might not work if the paths have "\\"" in them + /* character needs to be escaped */ + //*output_pointer++ = '\\'; + switch (*input_pointer) { case '\\': - *ptr2++ = '\\'; + *output_pointer = '\\'; break; case '\"': - *ptr2++ = '\"'; + *output_pointer = '\"'; break; case '\b': - *ptr2++ = '\b'; + *output_pointer = '\b'; break; case '\f': - *ptr2++ = '\f'; + *output_pointer = '\f'; break; case '\n': - *ptr2++ = '\n'; + *output_pointer = '\n'; break; case '\r': - *ptr2++ = '\r'; + *output_pointer = '\r'; break; case '\t': - *ptr2++ = '\t'; + *output_pointer = '\t'; break; default: - snprintf(ptr2, out_buf_size - (ptr2 - out), "u%04x", token); - ptr2 += 5; - break; /* escape and print */ + /* escape and print as unicode codepoint */ + snprintf((char *)output_pointer, output_length - (size_t)(output_pointer - output), "u%04x", *input_pointer); + output_pointer += 4; + break; } } } - // *ptr2++ = '\"'; // Modified to not put quotes around the string - *ptr2++ = 0; - return out; + output[output_length] = '\0'; + + return true; } + /* Invoke print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item, printbuffer *p) { return print_string_ptr(item->pAllocator, item->valuestring, p); } +static cJSON_bool print_string(const cJSON *const item, printbuffer *const p, bool *out_of_memory) { + return print_string_ptr((unsigned char *)item->valuestring, p, out_of_memory); +} -/* Declare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value, bool *out_of_memory); -static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_array(cJSON *item, const char *value, bool *out_of_memory); -static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_object(cJSON *item, const char *value, bool *out_of_memory); -static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p); +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory); +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory); +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory); +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory); +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory); +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory); /* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) { - while (in && *in && (unsigned char)*in <= 32) in++; - return in; +static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) { + if ((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } + + if (buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) { + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end, + cJSON_bool require_null_terminated, bool *out_of_memory) { + size_t buffer_length; + + if (NULL == value) { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return loader_cJSON_ParseWithLengthOpts(pAllocator, value, buffer_length, return_parse_end, require_null_terminated, + out_of_memory); } /* Parse an object - create a new root, and populate. */ -static cJSON *cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end, - int require_null_terminated, bool *out_of_memory) { - const char *end = 0; - cJSON *c = cJSON_New_Item(pAllocator); - // ep = 0; // commented out as it is unused - if (!c) { +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLengthOpts(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, + const char **return_parse_end, cJSON_bool require_null_terminated, bool *out_of_memory) { + parse_buffer buffer = {0, 0, 0, 0, 0}; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) { + goto fail; + } + + buffer.content = (const unsigned char *)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.pAllocator = pAllocator; + + item = cJSON_New_Item(pAllocator); + if (item == NULL) /* memory fail */ + { *out_of_memory = true; - return 0; /* memory fail */ + goto fail; } - end = parse_value(c, skip(value), out_of_memory); - if (!end) { - loader_cJSON_Delete(c); - return 0; - } /* parse failure. ep is set. */ + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)), out_of_memory)) { + /* parse failure. ep is set. */ + goto fail; + } - /* if we require null-terminated JSON without appended garbage, skip and - * then check for a null terminator */ + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) { - end = skip(end); - if (*end) { - loader_cJSON_Delete(c); - // ep = end; // commented out as it is unused - return 0; + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if (return_parse_end) { + *return_parse_end = (const char *)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) { + loader_cJSON_Delete(item); + } + + if (value != NULL) { + error local_error; + local_error.json = (const unsigned char *)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if (buffer.length > 0) { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) { + *return_parse_end = (const char *)local_error.json + local_error.position; } + + global_error = local_error; } - if (return_parse_end) *return_parse_end = end; - return c; + + return NULL; +} + +/* Default options for loader_cJSON_Parse */ +CJSON_PUBLIC(cJSON *) loader_cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) { + return loader_cJSON_ParseWithOpts(pAllocator, value, 0, 0, out_of_memory); } -/* Default options for cJSON_Parse */ -static cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) { - return cJSON_ParseWithOpts(pAllocator, value, 0, 0, out_of_memory); + +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLength(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, + bool *out_of_memory) { + return loader_cJSON_ParseWithLengthOpts(pAllocator, value, buffer_length, 0, 0, out_of_memory); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON *const item, cJSON_bool format, bool *out_of_memory) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char *)loader_calloc(item->pAllocator, default_buffer_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->pAllocator = item->pAllocator; + if (buffer->buffer == NULL) { + *out_of_memory = true; + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer, out_of_memory)) { + goto fail; + } + update_offset(buffer); + + printed = (unsigned char *)loader_realloc(item->pAllocator, buffer->buffer, buffer->length, buffer->offset + 1, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (printed == NULL) { + *out_of_memory = true; + goto fail; + } + buffer->buffer = NULL; + + return printed; + +fail: + if (buffer->buffer != NULL) { + loader_free(item->pAllocator, buffer->buffer); + buffer->buffer = NULL; + } + + if (printed != NULL) { + loader_free(item->pAllocator, printed); + printed = NULL; + } + + return NULL; } /* Render a cJSON item/entity/structure to text. */ -char *loader_cJSON_Print(cJSON *item) { return print_value(item, 0, 1, 0); } -char *loader_cJSON_PrintUnformatted(cJSON *item) { return print_value(item, 0, 0, 0); } +TEST_FUNCTION_EXPORT CJSON_PUBLIC(char *) loader_cJSON_Print(const cJSON *item, bool *out_of_memory) { + return (char *)print(item, true, out_of_memory); +} + +CJSON_PUBLIC(char *) loader_cJSON_PrintUnformatted(const cJSON *item, bool *out_of_memory) { + return (char *)print(item, false, out_of_memory); +} + +CJSON_PUBLIC(char *) +loader_cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt, bool *out_of_memory) { + printbuffer p = {0, 0, 0, 0, 0, 0, 0}; + + if (prebuffer < 0) { + return NULL; + } + + p.buffer = (unsigned char *)loader_alloc(item->pAllocator, (size_t)prebuffer, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (!p.buffer) { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.pAllocator = item->pAllocator; + + if (!print_value(item, &p, out_of_memory)) { + loader_free(item->pAllocator, p.buffer); + p.buffer = NULL; + return NULL; + } + + return (char *)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) +loader_cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, 0}; + + if ((length < 0) || (buffer == NULL)) { + return false; + } + + p.buffer = (unsigned char *)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.pAllocator = item->pAllocator; + bool out_of_memory = false; + return print_value(item, &p, &out_of_memory); +} /* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value, bool *out_of_memory) { - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value, "null", 4)) { +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) { item->type = cJSON_NULL; - return value + 4; + input_buffer->offset += 4; + return true; } - if (!strncmp(value, "false", 5)) { + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)) { item->type = cJSON_False; - return value + 5; + input_buffer->offset += 5; + return true; } - if (!strncmp(value, "true", 4)) { + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) { item->type = cJSON_True; item->valueint = 1; - return value + 4; + input_buffer->offset += 4; + return true; } - if (*value == '\"') { - return parse_string(item, value, out_of_memory); + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer, out_of_memory); } - if (*value == '-' || (*value >= '0' && *value <= '9')) { - return parse_number(item, value); + /* number */ + if (can_access_at_index(input_buffer, 0) && + ((buffer_at_offset(input_buffer)[0] == '-') || + ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); } - if (*value == '[') { - return parse_array(item, value, out_of_memory); + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer, out_of_memory); } - if (*value == '{') { - return parse_object(item, value, out_of_memory); + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer, out_of_memory); } - // ep = value; // commented out as it is unused - return 0; /* failure. */ + return false; } /* Render a value to text. */ -static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p) { - char *out = 0; - if (!item) return 0; - if (p) { - switch ((item->type) & 255) { - case cJSON_NULL: { - out = ensure(item->pAllocator, p, 5); - if (out) loader_strncpy(out, 5, "null", 5); - break; +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch ((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5, out_of_memory); + if (output == NULL) { + return false; } - case cJSON_False: { - out = ensure(item->pAllocator, p, 6); - if (out) loader_strncpy(out, 6, "false", 6); - break; + strcpy((char *)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6, out_of_memory); + if (output == NULL) { + return false; } - case cJSON_True: { - out = ensure(item->pAllocator, p, 5); - if (out) loader_strncpy(out, 5, "true", 5); - break; + strcpy((char *)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5, out_of_memory); + if (output == NULL) { + return false; } - case cJSON_Number: - out = print_number(item, p); - break; - case cJSON_String: - out = print_string(item, p); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, p); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, p); - break; - } - } else { - switch ((item->type) & 255) { - case cJSON_NULL: - out = cJSON_strdup(item->pAllocator, "null"); - break; - case cJSON_False: - out = cJSON_strdup(item->pAllocator, "false"); - break; - case cJSON_True: - out = cJSON_strdup(item->pAllocator, "true"); - break; - case cJSON_Number: - out = print_number(item, 0); - break; - case cJSON_String: - out = print_string(item, 0); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, 0); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, 0); - break; + strcpy((char *)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer, out_of_memory); + + case cJSON_Raw: { + size_t raw_length = 0; + if (item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length, out_of_memory); + if (output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; } + + case cJSON_String: + return print_string(item, output_buffer, out_of_memory); + + case cJSON_Array: + return print_array(item, output_buffer, out_of_memory); + + case cJSON_Object: + return print_object(item, output_buffer, out_of_memory); + + default: + return false; } - return out; } /* Build an array from input text. */ -static const char *parse_array(cJSON *item, const char *value, bool *out_of_memory) { - cJSON *child; - if (*value != '[') { - // ep = value; // commented out as it is unused - return 0; - } /* not an array! */ +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; - item->type = cJSON_Array; - value = skip(value + 1); - if (*value == ']') return value + 1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child = child = cJSON_New_Item(item->pAllocator); - if (!item->child) { - *out_of_memory = true; - return 0; /* memory fail */ + if (buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; } - value = skip(parse_value(child, skip(value), out_of_memory)); /* skip any spacing, get the value. */ - if (!value) return 0; - while (*value == ',') { - cJSON *new_item; - new_item = cJSON_New_Item(item->pAllocator); - if (!new_item) { + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(input_buffer->pAllocator); + if (new_item == NULL) { *out_of_memory = true; - return 0; /* memory fail */ + goto fail; /* allocation failure */ } - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_value(child, skip(value + 1), out_of_memory)); - if (!value) return 0; /* memory fail */ + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer, out_of_memory)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; } - if (*value == ']') return value + 1; /* end of array */ - // ep = value; // commented out as it is unused - return 0; /* malformed. */ + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) { + loader_cJSON_Delete(head); + } + + return false; } /* Render an array to text */ -static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p) { - char **entries; - char *out = 0, *ptr, *ret; - size_t len = 5; - cJSON *child = item->child; - int numentries = 0, fail = 0, j = 0; - size_t tmplen = 0, i = 0; - - /* How many entries in the array? */ - while (child) numentries++, child = child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) { - if (p) - out = ensure(item->pAllocator, p, 3); - else - out = (char *)cJSON_malloc(item->pAllocator, 3); - if (out) loader_strncpy(out, 3, "[]", 3); - return out; - } - - if (p) { - /* Compose the output array. */ - i = p->offset; - ptr = ensure(item->pAllocator, p, 1); - if (!ptr) return 0; - *ptr = '['; - p->offset++; - child = item->child; - while (child && !fail) { - print_value(child, depth + 1, fmt, p); - p->offset = cJSON_update(p); - if (child->next) { - len = fmt ? 2 : 1; - ptr = ensure(item->pAllocator, p, len + 1); - if (!ptr) return 0; - *ptr++ = ','; - if (fmt) *ptr++ = ' '; - *ptr = 0; - p->offset += len; - } - child = child->next; - } - ptr = ensure(item->pAllocator, p, 2); - if (!ptr) return 0; - *ptr++ = ']'; - *ptr = 0; - out = (p->buffer) + i; - } else { - /* Allocate an array to hold the values for each */ - entries = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *)); - if (!entries) return 0; - memset(entries, 0, numentries * sizeof(char *)); - /* Retrieve all the results: */ - child = item->child; - while (child && !fail) { - ret = print_value(child, depth + 1, fmt, 0); - entries[i++] = ret; - if (ret) - len += strlen(ret) + 2 + (fmt ? 1 : 0); - else - fail = 1; - child = child->next; - } +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; - /* If we didn't fail, try to malloc the output string */ - if (!fail) out = (char *)cJSON_malloc(item->pAllocator, len); - /* If that fails, we fail. */ - if (!out) fail = 1; + if (output_buffer == NULL) { + return false; + } - /* Handle failure. */ - if (fail) { - for (j = 0; j < numentries; j++) - if (entries[j]) cJSON_Free(item->pAllocator, entries[j]); - cJSON_Free(item->pAllocator, entries); - return 0; - } + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1, out_of_memory); + if (output_pointer == NULL) { + return false; + } - /* Compose the output array. */ - *out = '['; - ptr = out + 1; - *ptr = 0; - for (j = 0; j < numentries; j++) { - tmplen = strlen(entries[j]); - memcpy(ptr, entries[j], tmplen); - ptr += tmplen; - if (j != numentries - 1) { - *ptr++ = ','; - if (fmt) *ptr++ = ' '; - *ptr = 0; + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) { + if (!print_value(current_element, output_buffer, out_of_memory)) { + return false; + } + update_offset(output_buffer); + if (current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1, out_of_memory); + if (output_pointer == NULL) { + return false; } - cJSON_Free(item->pAllocator, entries[j]); + *output_pointer++ = ','; + if (output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; } - cJSON_Free(item->pAllocator, entries); - *ptr++ = ']'; - *ptr++ = 0; + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2, out_of_memory); + if (output_pointer == NULL) { + return false; } - return out; + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value, bool *out_of_memory) { - cJSON *child; - if (*value != '{') { - // ep = value; // commented out as it is unused - return 0; - } /* not an object! */ +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer, bool *out_of_memory) { + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; - item->type = cJSON_Object; - value = skip(value + 1); - if (*value == '}') return value + 1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child = child = cJSON_New_Item(item->pAllocator); - if (!item->child) { - *out_of_memory = true; - return 0; + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ } - value = skip(parse_string(child, skip(value), out_of_memory)); - if (!value) return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') { - // ep = value; // commented out as it is unused - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1), out_of_memory)); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value == ',') { - cJSON *new_item; - new_item = cJSON_New_Item(item->pAllocator); - if (!new_item) { + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(input_buffer->pAllocator); + if (new_item == NULL) { *out_of_memory = true; - return 0; /* memory fail */ + goto fail; /* allocation failure */ } - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_string(child, skip(value + 1), out_of_memory)); - if (!value) return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') { - // ep = value; // commented out as it is unused - return 0; - } /* fail! */ - value = skip(parse_value(child, skip(value + 1), out_of_memory)); /* skip any spacing, get the value. */ - if (!value) return 0; + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + if (cannot_access_at_index(input_buffer, 1)) { + goto fail; /* nothing comes after the comma */ + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer, out_of_memory)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer, out_of_memory)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; } - if (*value == '}') return value + 1; /* end of array */ - // ep = value; // commented out as it is unused - return 0; /* malformed. */ + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) { + loader_cJSON_Delete(head); + } + + return false; } /* Render an object to text. */ -static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p) { - char **entries = 0, **names = 0; - char *out = 0, *ptr, *ret, *str; - int j; - cJSON *child = item->child; - int numentries = 0, fail = 0, k; - size_t tmplen = 0, i = 0, len = 7; - /* Count the number of entries. */ - while (child) numentries++, child = child->next; - /* Explicitly handle empty object case */ - if (!numentries) { - if (p) - out = ensure(item->pAllocator, p, fmt ? depth + 4 : 3); - else - out = (char *)cJSON_malloc(item->pAllocator, fmt ? depth + 4 : 3); - if (!out) return 0; - ptr = out; - *ptr++ = '{'; - if (fmt) { - *ptr++ = '\n'; - for (j = 0; j < depth - 1; j++) *ptr++ = '\t'; - } - *ptr++ = '}'; - *ptr++ = 0; - return out; - } - if (p) { - /* Compose the output: */ - i = p->offset; - len = fmt ? 2 : 1; - ptr = ensure(item->pAllocator, p, len + 1); - if (!ptr) return 0; - *ptr++ = '{'; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - p->offset += len; - child = item->child; - depth++; - while (child) { - if (fmt) { - ptr = ensure(item->pAllocator, p, depth); - if (!ptr) return 0; - for (j = 0; j < depth; j++) *ptr++ = '\t'; - p->offset += depth; +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer, bool *out_of_memory) { + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1, out_of_memory); + if (output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) { + if (output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth, out_of_memory); + if (output_pointer == NULL) { + return false; + } + for (i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; } - print_string_ptr(item->pAllocator, child->string, p); - p->offset = cJSON_update(p); - - len = fmt ? 2 : 1; - ptr = ensure(item->pAllocator, p, len); - if (!ptr) return 0; - *ptr++ = ':'; - if (fmt) *ptr++ = '\t'; - p->offset += len; - - print_value(child, depth, fmt, p); - p->offset = cJSON_update(p); - - len = (fmt ? 1 : 0) + (child->next ? 1 : 0); - ptr = ensure(item->pAllocator, p, len + 1); - if (!ptr) return 0; - if (child->next) *ptr++ = ','; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - p->offset += len; - child = child->next; + output_buffer->offset += output_buffer->depth; } - ptr = ensure(item->pAllocator, p, fmt ? (depth + 1) : 2); - if (!ptr) return 0; - if (fmt) - for (j = 0; j < depth - 1; j++) *ptr++ = '\t'; - *ptr++ = '}'; - *ptr = 0; - out = (p->buffer) + i; - } else { - /* Allocate space for the names and the objects */ - entries = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *)); - if (!entries) return 0; - names = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *)); - if (!names) { - cJSON_Free(item->pAllocator, entries); - return 0; + + /* print key */ + if (!print_string_ptr((unsigned char *)current_item->string, output_buffer, out_of_memory)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length, out_of_memory); + if (output_pointer == NULL) { + return false; } - memset(entries, 0, sizeof(char *) * numentries); - memset(names, 0, sizeof(char *) * numentries); - - /* Collect all the results into our arrays: */ - child = item->child; - depth++; - if (fmt) len += depth; - while (child) { - names[i] = str = print_string_ptr(item->pAllocator, child->string, 0); - entries[i++] = ret = print_value(child, depth, fmt, 0); - if (str && ret) - len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); - else - fail = 1; - child = child->next; + *output_pointer++ = ':'; + if (output_buffer->format) { + *output_pointer++ = '\t'; } + output_buffer->offset += length; - /* Try to allocate the output string */ - if (!fail) out = (char *)cJSON_malloc(item->pAllocator, len); - if (!out) fail = 1; + /* print value */ + if (!print_value(current_item, output_buffer, out_of_memory)) { + return false; + } + update_offset(output_buffer); - /* Handle failure */ - if (fail) { - for (j = 0; j < numentries; j++) { - if (names[i]) cJSON_Free(item->pAllocator, names[j]); - if (entries[j]) cJSON_Free(item->pAllocator, entries[j]); - } - cJSON_Free(item->pAllocator, names); - cJSON_Free(item->pAllocator, entries); - return 0; + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1, out_of_memory); + if (output_pointer == NULL) { + return false; + } + if (current_item->next) { + *output_pointer++ = ','; } - /* Compose the output: */ - *out = '{'; - ptr = out + 1; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - for (j = 0; j < numentries; j++) { - if (fmt) - for (k = 0; k < depth; k++) *ptr++ = '\t'; - tmplen = strlen(names[j]); - memcpy(ptr, names[j], tmplen); - ptr += tmplen; - *ptr++ = ':'; - if (fmt) *ptr++ = '\t'; - size_t entries_size = strlen(entries[j]); - loader_strncpy(ptr, len - (ptr - out), entries[j], entries_size); - ptr += entries_size; - if (j != numentries - 1) *ptr++ = ','; - if (fmt) *ptr++ = '\n'; - *ptr = 0; - cJSON_Free(item->pAllocator, names[j]); - cJSON_Free(item->pAllocator, entries[j]); + if (output_buffer->format) { + *output_pointer++ = '\n'; } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } - cJSON_Free(item->pAllocator, names); - cJSON_Free(item->pAllocator, entries); - if (fmt) - for (j = 0; j < depth - 1; j++) *ptr++ = '\t'; - *ptr++ = '}'; - *ptr++ = 0; + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2, out_of_memory); + if (output_pointer == NULL) { + return false; + } + if (output_buffer->format) { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } } - return out; + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Get Array size/item / object item. */ -int loader_cJSON_GetArraySize(cJSON *array) { - cJSON *c = array->child; - int i = 0; - while (c) i++, c = c->next; - return i; +CJSON_PUBLIC(int) loader_cJSON_GetArraySize(const cJSON *array) { + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) { + return 0; + } + + child = array->child; + + while (child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; } -cJSON *loader_cJSON_GetArrayItem(cJSON *array, int item) { - cJSON *c = array->child; - while (c && item > 0) item--, c = c->next; - return c; + +static cJSON *get_array_item(const cJSON *array, size_t index) { + cJSON *current_child = NULL; + + if (array == NULL) { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; } -cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string) { - cJSON *c = object->child; - while (c && strcmp(c->string, string)) c = c->next; - return c; + +CJSON_PUBLIC(cJSON *) loader_cJSON_GetArrayItem(const cJSON *array, int index) { + if (index < 0) { + return NULL; + } + + return get_array_item(array, (size_t)index); } -VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) { - FILE *file = NULL; - char *json_buf = NULL; - size_t len; - VkResult res = VK_SUCCESS; +static cJSON *get_object_item(const cJSON *const object, const char *const name, const cJSON_bool case_sensitive) { + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if (case_sensitive) { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while ((current_element != NULL) && + (case_insensitive_strcmp((const unsigned char *)name, (const unsigned char *)(current_element->string)) != 0)) { + current_element = current_element->next; + } + } - assert(json != NULL); + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } - *json = NULL; + return current_element; +} -#if defined(_WIN32) - int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); - if (filename_utf16_size > 0) { - wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t)); - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) { - errno_t wfopen_error = _wfopen_s(&file, filename_utf16, L"rb"); - if (0 != wfopen_error) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); - } +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItem(const cJSON *const object, const char *const string) { + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_HasObjectItem(const cJSON *object, const char *string) { + return loader_cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +static void skip_oneline_comment(char **input) { + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; } } -#elif COMMON_UNIX_PLATFORMS - file = fopen(filename, "rb"); -#else -#warning fopen not available on this platform -#endif +} + +static void skip_multiline_comment(char **input) { + *input += static_strlen("/*"); - if (!file) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; + for (; (*input)[0] != '\0'; ++(*input)) { + if (((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } } - // NOTE: We can't just use fseek(file, 0, SEEK_END) because that isn't guaranteed to be supported on all systems - size_t fread_ret_count = 0; - do { - char buffer[256]; - fread_ret_count = fread(buffer, 1, 256, file); - } while (fread_ret_count == 256 && !feof(file)); - len = ftell(file); - fseek(file, 0, SEEK_SET); - json_buf = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (json_buf == NULL) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "loader_get_json: Failed to allocate space for JSON file %s buffer of length %lu", filename, len); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - if (fread(json_buf, sizeof(char), len, file) != len) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read JSON file %s.", filename); - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - json_buf[len] = '\0'; - - // Can't be a valid json if the string is of length zero - if (len == 0) { - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - // Parse text from file - bool out_of_memory = false; - *json = cJSON_Parse(inst ? &inst->alloc_callbacks : NULL, json_buf, &out_of_memory); - if (out_of_memory) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.", - filename); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } else if (*json == NULL) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename); - goto out; +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } } +} -out: - loader_instance_heap_free(inst, json_buf); - if (NULL != file) { - fclose(file); +CJSON_PUBLIC(void) loader_cJSON_Minify(char *json) { + char *into = json; + + if (json == NULL) { + return; } - if (res != VK_SUCCESS && *json != NULL) { - loader_cJSON_Delete(*json); - *json = NULL; + + while (json[0] != '\0') { + switch (json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') { + skip_oneline_comment(&json); + } else if (json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char **)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } } - return res; + /* and null-terminate. */ + *into = '\0'; } -VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, - size_t out_str_len, char *out_string) { - cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { - return VK_ERROR_INITIALIZATION_FAILED; +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsInvalid(const cJSON *const item) { + if (item == NULL) { + return false; } - char *str = loader_cJSON_Print(item); - if (str == NULL) { - return VK_ERROR_OUT_OF_HOST_MEMORY; + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsFalse(const cJSON *const item) { + if (item == NULL) { + return false; } - if (NULL != out_string) { - loader_strncpy(out_string, out_str_len, str, out_str_len); - if (out_str_len > 0) { - out_string[out_str_len - 1] = '\0'; - } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsTrue(const cJSON *const item) { + if (item == NULL) { + return false; } - loader_instance_heap_free(inst, str); - return VK_SUCCESS; + + return (item->type & 0xff) == cJSON_True; } -VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) { - cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { - return VK_ERROR_INITIALIZATION_FAILED; +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsBool(const cJSON *const item) { + if (item == NULL) { + return false; } - char *str = loader_cJSON_Print(item); - if (str == NULL) { - return VK_ERROR_OUT_OF_HOST_MEMORY; + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNull(const cJSON *const item) { + if (item == NULL) { + return false; } - if (NULL != out_string) { - *out_string = str; + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNumber(const cJSON *const item) { + if (item == NULL) { + return false; } - return VK_SUCCESS; + + return (item->type & 0xFF) == cJSON_Number; } -VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, - struct loader_string_list *string_list) { - VkResult res = VK_SUCCESS; - cJSON *item = loader_cJSON_GetObjectItem(object, key); - if (NULL == item) { - return VK_ERROR_INITIALIZATION_FAILED; + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsString(const cJSON *const item) { + if (item == NULL) { + return false; } - uint32_t count = loader_cJSON_GetArraySize(item); - if (count == 0) { - return VK_SUCCESS; + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsArray(const cJSON *const item) { + if (item == NULL) { + return false; } - res = create_string_list(inst, count, string_list); - if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { - goto out; + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item) { + if (item == NULL) { + return false; } - for (uint32_t i = 0; i < count; i++) { - cJSON *element = loader_cJSON_GetArrayItem(item, i); - if (element == NULL) { - return VK_ERROR_INITIALIZATION_FAILED; - } - char *out_data = loader_cJSON_Print(element); - if (out_data == NULL) { - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - res = append_str_to_string_list(inst, string_list, out_data); - if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { - goto out; - } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsRaw(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) loader_cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; } -out: - if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) { - free_string_list(inst, string_list); + + /* identical objects are equal */ + if (a == b) { + return true; } - return res; + switch (a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) { + if (!loader_cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) { + return false; + } + + if (!loader_cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) { + return false; + } + + if (!loader_cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } } diff --git a/loader/cJSON.h b/loader/cJSON.h index e02af8a71839e559e1e72cb6fc28a7a531124d36..457d669a5839fc439b29375894d7cecb4afb3874 100644 --- a/loader/cJSON.h +++ b/loader/cJSON.h @@ -1,8 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble - Copyright (c) 2015-2021 The Khronos Group Inc. - Copyright (c) 2015-2021 Valve Corporation - Copyright (c) 2015-2021 LunarG, Inc. + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,89 +20,201 @@ THE SOFTWARE. */ -#pragma once +#ifndef cJSON__h +#define cJSON__h -#include +#ifdef __cplusplus +extern "C" { +#endif -#include +#define CJSON_HIDE_SYMBOLS + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project +with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 18 + +#include +#include + +#include "vk_loader_platform.h" /* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 +/* loader type declarations for allocation hooks */ +typedef struct VkAllocationCallbacks VkAllocationCallbacks; +struct loader_instance; + /* The cJSON structure: */ typedef struct cJSON { - struct cJSON *next, *prev; /* next/prev allow you to walk array/object - chains. Alternatively, use - GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer - pointing to a chain of the items in the - array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, or is - in the list of subitems of an object. */ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; /* pointer to the allocation callbacks to use */ - VkAllocationCallbacks *pAllocator; + const VkAllocationCallbacks *pAllocator; } cJSON; -/* Render a cJSON entity to text for transfer/storage. Free the char* when - * finished. */ -char *loader_cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. - * Free the char* when finished. */ -char *loader_cJSON_PrintUnformatted(cJSON *item); +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + +/* Memory Management: the caller is always responsible to free instthe results from all variants of loader_cJSON_Parse (with + * loader_cJSON_Delete) and loader_loader_cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The + * exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) loader_cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory); +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLength(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, bool *out_of_memory); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte + * parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will + * match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end, + cJSON_bool require_null_terminated, bool *out_of_memory); +CJSON_PUBLIC(cJSON *) +loader_cJSON_ParseWithLengthOpts(const VkAllocationCallbacks *pAllocator, const char *value, size_t buffer_length, + const char **return_parse_end, cJSON_bool require_null_terminated, bool *out_of_memory); + +/* Render a cJSON entity to text for transfer/storage. */ +TEST_FUNCTION_EXPORT CJSON_PUBLIC(char *) loader_cJSON_Print(const cJSON *item, bool *out_of_memory); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) loader_cJSON_PrintUnformatted(const cJSON *item, bool *out_of_memory); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces + * reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) +loader_cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt, bool *out_of_memory); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on + * failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you + * actually need */ +CJSON_PUBLIC(cJSON_bool) +loader_cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -void loader_cJSON_Delete(cJSON *c); +TEST_FUNCTION_EXPORT CJSON_PUBLIC(void) loader_cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ -int loader_cJSON_GetArraySize(cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. - */ -cJSON *loader_cJSON_GetArrayItem(cJSON *array, int item); +CJSON_PUBLIC(int) loader_cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) loader_cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ -cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string); - -/* When assigning an integer value, it needs to be propagated to valuedouble - * too. */ -#define cJSON_SetIntValue(object, val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) -#define cJSON_SetNumberValue(object, val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) - -// Helper functions to using JSON - -struct loader_instance; -struct loader_string_list; - -// Read a JSON file into a buffer. -// -// @return - A pointer to a cJSON object representing the JSON parse tree. -// This returned buffer should be freed by caller. -VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json); - -// Given a cJSON object, find the string associated with the key and puts an pre-allocated string into out_string. -// Length is given by out_str_len, and this function truncates the string with a null terminator if it the provided space isn't -// large enough. -VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key, - size_t out_str_len, char *out_string); - -// Given a cJSON object, find the string associated with the key and puts an allocated string into out_string. -// It is the callers responsibility to free out_string. -VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string); - -// Given a cJSON object, find the array of strings associated with they key and writes the count into out_count and data into -// out_array_of_strings. It is the callers responsibility to free out_array_of_strings. -VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, - struct loader_string_list *string_list); +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItem(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON *) loader_cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make + * sense of it. Defined when loader_cJSON_Parse() returns 0. 0 when loader_cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) loader_cJSON_GetStringValue(const cJSON *const item); +CJSON_PUBLIC(double) loader_cJSON_GetNumberValue(const cJSON *const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsInvalid(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsFalse(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsTrue(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsBool(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNull(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsNumber(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsString(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsArray(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsObject(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) loader_cJSON_IsRaw(const cJSON *const item); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) \ + for (element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/loader/debug_utils.c b/loader/debug_utils.c index 341dbb1b4d76937ae0f9fd2f1bc9de59f63b130b..1506dc53034e56b836d5712b0a50ccd11afac863 100644 --- a/loader/debug_utils.c +++ b/loader/debug_utils.c @@ -27,11 +27,7 @@ #include #include #include -#if !defined(WIN32) -#include -#endif -#include "vulkan/vk_layer.h" #include "vk_object_types.h" #include "allocation.h" @@ -92,7 +88,6 @@ VkBool32 util_SubmitDebugUtilsMessageEXT(const struct loader_instance *inst, VkD if (0 < pCallbackData->objectCount) { debug_utils_AnnotObjectToDebugReportObject(pCallbackData->pObjects, &object_type, &object_handle); } - while (pTrav) { if (pTrav->is_messenger && (pTrav->messenger.messageSeverity & messageSeverity) && (pTrav->messenger.messageType & messageTypes)) { @@ -106,7 +101,6 @@ VkBool32 util_SubmitDebugUtilsMessageEXT(const struct loader_instance *inst, VkD bail = true; } } - pTrav = pTrav->pNext; } } @@ -136,7 +130,9 @@ VkResult util_CreateDebugUtilsMessengers(struct loader_instance *inst, const voi const VkAllocationCallbacks *pAllocator) { const void *pNext = pChain; while (pNext) { - if (((const VkBaseInStructure *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) { + VkBaseInStructure in_structure = {0}; + memcpy(&in_structure, pNext, sizeof(VkBaseInStructure)); + if (in_structure.sType == VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) { // Assign a unique handle to each messenger (just use the address of the VkDebugUtilsMessengerCreateInfoEXT) // This is only being used this way due to it being for an 'anonymous' callback during instance creation VkDebugUtilsMessengerEXT messenger_handle = (VkDebugUtilsMessengerEXT)(uintptr_t)pNext; @@ -146,7 +142,7 @@ VkResult util_CreateDebugUtilsMessengers(struct loader_instance *inst, const voi return ret; } } - pNext = (void *)((VkBaseInStructure *)pNext)->pNext; + pNext = in_structure.pNext; } return VK_SUCCESS; } @@ -175,33 +171,44 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugUtilsMessengerEXT(VkInstanc const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDebugUtilsMessengerEXT *pMessenger) { - VkDebugUtilsMessengerEXT *icd_info = NULL; - const struct loader_icd_term *icd_term; struct loader_instance *inst = (struct loader_instance *)instance; VkResult res = VK_SUCCESS; - uint32_t storage_idx; VkLayerDbgFunctionNode *new_dbg_func_node = NULL; + uint32_t next_index = 0; - icd_info = (VkDebugUtilsMessengerEXT *)loader_calloc_with_instance_fallback( - pAllocator, inst, inst->total_icd_count * sizeof(VkDebugUtilsMessengerEXT), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); - - if (!icd_info) { + uint32_t *pNextIndex = loader_instance_heap_alloc(inst, sizeof(uint32_t), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (NULL == pNextIndex) { res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } - storage_idx = 0; - for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { - if (!icd_term->dispatch.CreateDebugUtilsMessengerEXT) { - continue; + res = loader_get_next_available_entry(inst, &inst->debug_utils_messengers_list, &next_index, pAllocator); + if (res != VK_SUCCESS) { + goto out; + } + + for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { + if (icd_term->debug_utils_messenger_list.list == NULL) { + res = loader_init_generic_list(inst, (struct loader_generic_list *)&icd_term->debug_utils_messenger_list, + sizeof(VkDebugUtilsMessengerEXT)); + if (res != VK_SUCCESS) { + goto out; + } + } else if (icd_term->debug_utils_messenger_list.capacity <= next_index * sizeof(VkDebugUtilsMessengerEXT)) { + res = loader_resize_generic_list(inst, (struct loader_generic_list *)&icd_term->debug_utils_messenger_list); + if (res != VK_SUCCESS) { + goto out; + } } - res = icd_term->dispatch.CreateDebugUtilsMessengerEXT(icd_term->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]); + if (icd_term->dispatch.CreateDebugUtilsMessengerEXT) { + res = icd_term->dispatch.CreateDebugUtilsMessengerEXT(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->debug_utils_messenger_list.list[next_index]); - if (res != VK_SUCCESS) { - goto out; + if (res != VK_SUCCESS) { + goto out; + } } - storage_idx++; } // Setup the debug report callback in the terminator since a layer may want @@ -221,27 +228,32 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugUtilsMessengerEXT(VkInstanc new_dbg_func_node->pUserData = pCreateInfo->pUserData; new_dbg_func_node->pNext = inst->current_dbg_function_head; inst->current_dbg_function_head = new_dbg_func_node; - - *pMessenger = (VkDebugUtilsMessengerEXT)(uintptr_t)icd_info; + *pNextIndex = next_index; + *pMessenger = (VkDebugUtilsMessengerEXT)(uintptr_t)pNextIndex; new_dbg_func_node->messenger.messenger = *pMessenger; out: // Roll back on errors if (VK_SUCCESS != res) { - storage_idx = 0; - for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { - if (NULL == icd_term->dispatch.DestroyDebugUtilsMessengerEXT) { - continue; + if (pNextIndex) { + for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { + if (icd_term->debug_utils_messenger_list.list && icd_term->debug_utils_messenger_list.list[next_index] && + NULL != icd_term->dispatch.DestroyDebugUtilsMessengerEXT) { + icd_term->dispatch.DestroyDebugUtilsMessengerEXT( + icd_term->instance, icd_term->debug_utils_messenger_list.list[next_index], pAllocator); + } } - - if (icd_info && icd_info[storage_idx]) { - icd_term->dispatch.DestroyDebugUtilsMessengerEXT(icd_term->instance, icd_info[storage_idx], pAllocator); + } + if (inst->debug_utils_messengers_list.list && + inst->debug_utils_messengers_list.capacity > (*pNextIndex) * sizeof(struct loader_used_object_status)) { + inst->debug_utils_messengers_list.list[*pNextIndex].status = VK_FALSE; + if (NULL != pAllocator) { + inst->debug_utils_messengers_list.list[*pNextIndex].allocation_callbacks = *pAllocator; } - storage_idx++; } loader_free_with_instance_fallback(pAllocator, inst, new_dbg_func_node); - loader_free_with_instance_fallback(pAllocator, inst, icd_info); + loader_free_with_instance_fallback(pAllocator, inst, pNextIndex); } return res; @@ -250,27 +262,31 @@ out: // This is the instance chain terminator function for DestroyDebugUtilsMessenger VKAPI_ATTR void VKAPI_CALL terminator_DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks *pAllocator) { - uint32_t storage_idx; - VkDebugUtilsMessengerEXT *icd_info; - const struct loader_icd_term *icd_term; - struct loader_instance *inst = (struct loader_instance *)instance; - icd_info = (VkDebugUtilsMessengerEXT *)(uintptr_t)messenger; - storage_idx = 0; - for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { - if (NULL == icd_term->dispatch.DestroyDebugUtilsMessengerEXT) { - continue; - } + uint32_t *debug_messenger_index = (uint32_t *)(uintptr_t)messenger; + // Make sure that messenger actually points to anything + if (NULL == debug_messenger_index) { + return; + } - if (icd_info && icd_info[storage_idx]) { - icd_term->dispatch.DestroyDebugUtilsMessengerEXT(icd_term->instance, icd_info[storage_idx], pAllocator); + for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { + if (icd_term->debug_utils_messenger_list.list && icd_term->debug_utils_messenger_list.list[*debug_messenger_index] && + NULL != icd_term->dispatch.DestroyDebugUtilsMessengerEXT) { + icd_term->dispatch.DestroyDebugUtilsMessengerEXT( + icd_term->instance, icd_term->debug_utils_messenger_list.list[*debug_messenger_index], pAllocator); } - storage_idx++; } util_DestroyDebugUtilsMessenger(inst, messenger, pAllocator); + if (inst->debug_utils_messengers_list.list && + inst->debug_utils_messengers_list.capacity > (*debug_messenger_index) * sizeof(struct loader_used_object_status)) { + inst->debug_utils_messengers_list.list[*debug_messenger_index].status = VK_FALSE; + if (NULL != pAllocator) { + inst->debug_utils_messengers_list.list[*debug_messenger_index].allocation_callbacks = *pAllocator; + } + } - loader_free_with_instance_fallback(pAllocator, inst, icd_info); + loader_free_with_instance_fallback(pAllocator, inst, debug_messenger_index); } // This is the instance chain terminator function for SubmitDebugUtilsMessageEXT @@ -391,7 +407,9 @@ VkResult util_CreateDebugReportCallbacks(struct loader_instance *inst, const voi const VkAllocationCallbacks *pAllocator) { const void *pNext = pChain; while (pNext) { - if (((VkBaseInStructure *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) { + VkBaseInStructure in_structure = {0}; + memcpy(&in_structure, pNext, sizeof(VkBaseInStructure)); + if (in_structure.sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) { // Assign a unique handle to each callback (just use the address of the VkDebugReportCallbackCreateInfoEXT): // This is only being used this way due to it being for an 'anonymous' callback during instance creation VkDebugReportCallbackEXT report_handle = (VkDebugReportCallbackEXT)(uintptr_t)pNext; @@ -401,7 +419,7 @@ VkResult util_CreateDebugReportCallbacks(struct loader_instance *inst, const voi return ret; } } - pNext = (void *)((VkBaseInStructure *)pNext)->pNext; + pNext = in_structure.pNext; } return VK_SUCCESS; } @@ -431,32 +449,44 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallbackEXT(VkInstanc const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) { - VkDebugReportCallbackEXT *icd_info = NULL; - const struct loader_icd_term *icd_term; struct loader_instance *inst = (struct loader_instance *)instance; VkResult res = VK_SUCCESS; - uint32_t storage_idx; VkLayerDbgFunctionNode *new_dbg_func_node = NULL; + uint32_t next_index = 0; - icd_info = ((VkDebugReportCallbackEXT *)loader_calloc_with_instance_fallback( - pAllocator, inst, inst->total_icd_count * sizeof(VkDebugReportCallbackEXT), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)); - if (!icd_info) { + uint32_t *pNextIndex = loader_instance_heap_alloc(inst, sizeof(uint32_t), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (NULL == pNextIndex) { res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } - storage_idx = 0; - for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { - if (!icd_term->dispatch.CreateDebugReportCallbackEXT) { - continue; + res = loader_get_next_available_entry(inst, &inst->debug_report_callbacks_list, &next_index, pAllocator); + if (res != VK_SUCCESS) { + goto out; + } + + for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { + if (icd_term->debug_report_callback_list.list == NULL) { + res = loader_init_generic_list(inst, (struct loader_generic_list *)&icd_term->debug_report_callback_list, + sizeof(VkDebugUtilsMessengerEXT)); + if (res != VK_SUCCESS) { + goto out; + } + } else if (icd_term->debug_report_callback_list.capacity <= next_index * sizeof(VkDebugReportCallbackEXT)) { + res = loader_resize_generic_list(inst, (struct loader_generic_list *)&icd_term->debug_report_callback_list); + if (res != VK_SUCCESS) { + goto out; + } } - res = icd_term->dispatch.CreateDebugReportCallbackEXT(icd_term->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]); + if (icd_term->dispatch.CreateDebugReportCallbackEXT) { + res = icd_term->dispatch.CreateDebugReportCallbackEXT(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->debug_report_callback_list.list[next_index]); - if (res != VK_SUCCESS) { - goto out; + if (res != VK_SUCCESS) { + goto out; + } } - storage_idx++; } // Setup the debug report callback in the terminator since a layer may want @@ -476,27 +506,32 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallbackEXT(VkInstanc new_dbg_func_node->pUserData = pCreateInfo->pUserData; new_dbg_func_node->pNext = inst->current_dbg_function_head; inst->current_dbg_function_head = new_dbg_func_node; - - *pCallback = (VkDebugReportCallbackEXT)(uintptr_t)icd_info; + *pNextIndex = next_index; + *pCallback = (VkDebugReportCallbackEXT)(uintptr_t)pNextIndex; new_dbg_func_node->report.msgCallback = *pCallback; out: // Roll back on errors if (VK_SUCCESS != res) { - storage_idx = 0; - for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { - if (NULL == icd_term->dispatch.DestroyDebugReportCallbackEXT) { - continue; + if (pNextIndex) { + for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { + if (icd_term->debug_report_callback_list.list && icd_term->debug_report_callback_list.list[next_index] && + NULL != icd_term->dispatch.DestroyDebugReportCallbackEXT) { + icd_term->dispatch.DestroyDebugReportCallbackEXT( + icd_term->instance, icd_term->debug_report_callback_list.list[next_index], pAllocator); + } } - - if (icd_info && icd_info[storage_idx]) { - icd_term->dispatch.DestroyDebugReportCallbackEXT(icd_term->instance, icd_info[storage_idx], pAllocator); + } + if (inst->debug_report_callbacks_list.list && + inst->debug_report_callbacks_list.capacity > (*pNextIndex) * sizeof(struct loader_used_object_status)) { + inst->debug_report_callbacks_list.list[*pNextIndex].status = VK_FALSE; + if (NULL != pAllocator) { + inst->debug_report_callbacks_list.list[*pNextIndex].allocation_callbacks = *pAllocator; } - storage_idx++; } loader_free_with_instance_fallback(pAllocator, inst, new_dbg_func_node); - loader_free_with_instance_fallback(pAllocator, inst, icd_info); + loader_free_with_instance_fallback(pAllocator, inst, pNextIndex); } return res; @@ -505,27 +540,29 @@ out: // This is the instance chain terminator function for DestroyDebugReportCallback VKAPI_ATTR void VKAPI_CALL terminator_DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks *pAllocator) { - uint32_t storage_idx; - VkDebugReportCallbackEXT *icd_info; - const struct loader_icd_term *icd_term; - struct loader_instance *inst = (struct loader_instance *)instance; - icd_info = (VkDebugReportCallbackEXT *)(uintptr_t)callback; - storage_idx = 0; - for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { - if (NULL == icd_term->dispatch.DestroyDebugReportCallbackEXT) { - continue; - } - - if (icd_info[storage_idx]) { - icd_term->dispatch.DestroyDebugReportCallbackEXT(icd_term->instance, icd_info[storage_idx], pAllocator); + uint32_t *debug_report_index = (uint32_t *)(uintptr_t)callback; + // Make sure that callback actually points to anything + if (NULL == debug_report_index) { + return; + } + for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { + if (icd_term->debug_report_callback_list.list && icd_term->debug_report_callback_list.list[*debug_report_index] && + NULL != icd_term->dispatch.DestroyDebugReportCallbackEXT) { + icd_term->dispatch.DestroyDebugReportCallbackEXT( + icd_term->instance, icd_term->debug_report_callback_list.list[*debug_report_index], pAllocator); } - storage_idx++; } util_DestroyDebugReportCallback(inst, callback, pAllocator); - - loader_free_with_instance_fallback(pAllocator, inst, icd_info); + if (inst->debug_report_callbacks_list.list && + inst->debug_report_callbacks_list.capacity > (*debug_report_index) * sizeof(struct loader_used_object_status)) { + inst->debug_report_callbacks_list.list[*debug_report_index].status = VK_FALSE; + if (NULL != pAllocator) { + inst->debug_report_callbacks_list.list[*debug_report_index].allocation_callbacks = *pAllocator; + } + } + loader_free_with_instance_fallback(pAllocator, inst, debug_report_index); } // This is the instance chain terminator function for DebugReportMessage diff --git a/loader/dev_ext_trampoline.c b/loader/dev_ext_trampoline.c index ae284671366770dfc340ae657956febc36f7f699..91ad38495256847effe8677b2d62da00e605ef3b 100644 --- a/loader/dev_ext_trampoline.c +++ b/loader/dev_ext_trampoline.c @@ -19,7 +19,7 @@ * Author: Lenny Komow */ -#include "loader.h" +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC optimize(3) // force gcc to use tail-calls #endif diff --git a/loader/dirent_on_windows.c b/loader/dirent_on_windows.c index 82309cb2edfdf02129557209a1a0e17e06facfee..36d3564ea63e01728802ea26fdea17e886c1855f 100644 --- a/loader/dirent_on_windows.c +++ b/loader/dirent_on_windows.c @@ -92,6 +92,10 @@ struct dirent *readdir(DIR *dir) { result = &dir->result; result->d_name = dir->info.name; } + // _findnext sets errno to ENOENT when no more matching files could be found, does not indicate an error + if (errno == ENOENT) { + errno = 0; + } } else { errno = EBADF; } diff --git a/loader/extension_manual.c b/loader/extension_manual.c index b8ed8a33eb41ad784349f7002ac9ef8606e5e1c6..6b917fcd0a675f4f891bdaac2791c2004280fc03 100644 --- a/loader/extension_manual.c +++ b/loader/extension_manual.c @@ -27,7 +27,6 @@ #include #include "allocation.h" -#include "debug_utils.h" #include "loader.h" #include "log.h" #include "wsi.h" @@ -107,12 +106,13 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2E struct loader_icd_term *icd_term = phys_dev_term->this_icd_term; VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(surface); - uint8_t icd_index = phys_dev_term->icd_index; // Unwrap the surface if needed VkSurfaceKHR unwrapped_surface = surface; - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)(icd_surface->real_icd_surfaces[icd_index])) { - unwrapped_surface = icd_surface->real_icd_surfaces[icd_index]; + if (NULL != phys_dev_term->this_icd_term->surface_list.list && + phys_dev_term->this_icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index]) { + unwrapped_surface = phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index]; } if (NULL != icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2EXT) { @@ -274,15 +274,18 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfacePresentModes2E "ICD associated with VkPhysicalDevice does not support GetPhysicalDeviceSurfacePresentModes2EXT"); abort(); } - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(pSurfaceInfo->surface); - uint8_t icd_index = phys_dev_term->icd_index; - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)icd_surface->real_icd_surfaces[icd_index]) { - VkPhysicalDeviceSurfaceInfo2KHR surface_info_copy; - surface_info_copy.sType = pSurfaceInfo->sType; - surface_info_copy.pNext = pSurfaceInfo->pNext; - surface_info_copy.surface = icd_surface->real_icd_surfaces[icd_index]; - return icd_term->dispatch.GetPhysicalDeviceSurfacePresentModes2EXT(phys_dev_term->phys_dev, &surface_info_copy, - pPresentModeCount, pPresentModes); + if (VK_NULL_HANDLE != pSurfaceInfo->surface) { + VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(pSurfaceInfo->surface); + if (NULL != icd_surface && NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { + VkPhysicalDeviceSurfaceInfo2KHR surface_info_copy; + surface_info_copy.sType = pSurfaceInfo->sType; + surface_info_copy.pNext = pSurfaceInfo->pNext; + surface_info_copy.surface = icd_term->surface_list.list[icd_surface->surface_index]; + return icd_term->dispatch.GetPhysicalDeviceSurfacePresentModes2EXT(phys_dev_term->phys_dev, &surface_info_copy, + pPresentModeCount, pPresentModes); + } } return icd_term->dispatch.GetPhysicalDeviceSurfacePresentModes2EXT(phys_dev_term->phys_dev, pSurfaceInfo, pPresentModeCount, pPresentModes); @@ -304,9 +307,8 @@ VKAPI_ATTR VkResult VKAPI_CALL GetDeviceGroupSurfacePresentModes2EXT(VkDevice de VKAPI_ATTR VkResult VKAPI_CALL terminator_GetDeviceGroupSurfacePresentModes2EXT(VkDevice device, const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, VkDeviceGroupPresentModeFlagsKHR *pModes) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev || NULL == dev->loader_dispatch.extension_terminator_dispatch.GetDeviceGroupSurfacePresentModes2EXT) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -321,14 +323,18 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetDeviceGroupSurfacePresentModes2EXT( "[VUID-vkGetDeviceGroupSurfacePresentModes2EXT-pSurfaceInfo-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pSurfaceInfo->surface; - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)icd_surface->real_icd_surfaces[icd_index]) { - VkPhysicalDeviceSurfaceInfo2KHR surface_info_copy; - surface_info_copy.sType = pSurfaceInfo->sType; - surface_info_copy.pNext = pSurfaceInfo->pNext; - surface_info_copy.surface = icd_surface->real_icd_surfaces[icd_index]; - return dev->loader_dispatch.extension_terminator_dispatch.GetDeviceGroupSurfacePresentModes2EXT(device, &surface_info_copy, - pModes); + if (VK_NULL_HANDLE != pSurfaceInfo->surface) { + VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(pSurfaceInfo->surface); + if (NULL != icd_surface && NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { + VkPhysicalDeviceSurfaceInfo2KHR surface_info_copy; + surface_info_copy.sType = pSurfaceInfo->sType; + surface_info_copy.pNext = pSurfaceInfo->pNext; + surface_info_copy.surface = icd_term->surface_list.list[icd_surface->surface_index]; + return dev->loader_dispatch.extension_terminator_dispatch.GetDeviceGroupSurfacePresentModes2EXT( + device, &surface_info_copy, pModes); + } } return dev->loader_dispatch.extension_terminator_dispatch.GetDeviceGroupSurfacePresentModes2EXT(device, pSurfaceInfo, pModes); } diff --git a/loader/generated/vk_layer_dispatch_table.h b/loader/generated/vk_layer_dispatch_table.h index e5e51630e4724b9958122436c2d756622440ef98..275cf42db4f23e4bf296d21ffcae65fab98846b0 100644 --- a/loader/generated/vk_layer_dispatch_table.h +++ b/loader/generated/vk_layer_dispatch_table.h @@ -27,6 +27,8 @@ // clang-format off #pragma once +#include + #if !defined(PFN_GetPhysicalDeviceProcAddr) typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName); #endif @@ -293,6 +295,12 @@ typedef struct VkLayerInstanceDispatchTable_ { #ifdef VK_USE_PLATFORM_OHOS PFN_vkCreateSurfaceOHOS CreateSurfaceOHOS; #endif // VK_USE_PLATFORM_OHOS + + // ---- VK_NV_cooperative_vector extension commands + PFN_vkGetPhysicalDeviceCooperativeVectorPropertiesNV GetPhysicalDeviceCooperativeVectorPropertiesNV; + + // ---- VK_NV_cooperative_matrix2 extension commands + PFN_vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV; } VkLayerInstanceDispatchTable; // Device function pointer dispatch table @@ -494,6 +502,27 @@ typedef struct VkLayerDispatchTable_ { PFN_vkGetDeviceImageMemoryRequirements GetDeviceImageMemoryRequirements; PFN_vkGetDeviceImageSparseMemoryRequirements GetDeviceImageSparseMemoryRequirements; + // ---- Core Vulkan 1.4 commands + PFN_vkCmdSetLineStipple CmdSetLineStipple; + PFN_vkMapMemory2 MapMemory2; + PFN_vkUnmapMemory2 UnmapMemory2; + PFN_vkCmdBindIndexBuffer2 CmdBindIndexBuffer2; + PFN_vkGetRenderingAreaGranularity GetRenderingAreaGranularity; + PFN_vkGetDeviceImageSubresourceLayout GetDeviceImageSubresourceLayout; + PFN_vkGetImageSubresourceLayout2 GetImageSubresourceLayout2; + PFN_vkCmdPushDescriptorSet CmdPushDescriptorSet; + PFN_vkCmdPushDescriptorSetWithTemplate CmdPushDescriptorSetWithTemplate; + PFN_vkCmdSetRenderingAttachmentLocations CmdSetRenderingAttachmentLocations; + PFN_vkCmdSetRenderingInputAttachmentIndices CmdSetRenderingInputAttachmentIndices; + PFN_vkCmdBindDescriptorSets2 CmdBindDescriptorSets2; + PFN_vkCmdPushConstants2 CmdPushConstants2; + PFN_vkCmdPushDescriptorSet2 CmdPushDescriptorSet2; + PFN_vkCmdPushDescriptorSetWithTemplate2 CmdPushDescriptorSetWithTemplate2; + PFN_vkCopyMemoryToImage CopyMemoryToImage; + PFN_vkCopyImageToMemory CopyImageToMemory; + PFN_vkCopyImageToImage CopyImageToImage; + PFN_vkTransitionImageLayout TransitionImageLayout; + // ---- VK_KHR_swapchain extension commands PFN_vkCreateSwapchainKHR CreateSwapchainKHR; PFN_vkDestroySwapchainKHR DestroySwapchainKHR; @@ -620,6 +649,10 @@ typedef struct VkLayerDispatchTable_ { // ---- VK_KHR_fragment_shading_rate extension commands PFN_vkCmdSetFragmentShadingRateKHR CmdSetFragmentShadingRateKHR; + // ---- VK_KHR_dynamic_rendering_local_read extension commands + PFN_vkCmdSetRenderingAttachmentLocationsKHR CmdSetRenderingAttachmentLocationsKHR; + PFN_vkCmdSetRenderingInputAttachmentIndicesKHR CmdSetRenderingInputAttachmentIndicesKHR; + // ---- VK_KHR_present_wait extension commands PFN_vkWaitForPresentKHR WaitForPresentKHR; @@ -655,8 +688,6 @@ typedef struct VkLayerDispatchTable_ { PFN_vkCmdPipelineBarrier2KHR CmdPipelineBarrier2KHR; PFN_vkCmdWriteTimestamp2KHR CmdWriteTimestamp2KHR; PFN_vkQueueSubmit2KHR QueueSubmit2KHR; - PFN_vkCmdWriteBufferMarker2AMD CmdWriteBufferMarker2AMD; - PFN_vkGetQueueCheckpointData2NV GetQueueCheckpointData2NV; // ---- VK_KHR_copy_commands2 extension commands PFN_vkCmdCopyBuffer2KHR CmdCopyBuffer2KHR; @@ -680,6 +711,16 @@ typedef struct VkLayerDispatchTable_ { PFN_vkGetDeviceImageSubresourceLayoutKHR GetDeviceImageSubresourceLayoutKHR; PFN_vkGetImageSubresourceLayout2KHR GetImageSubresourceLayout2KHR; + // ---- VK_KHR_pipeline_binary extension commands + PFN_vkCreatePipelineBinariesKHR CreatePipelineBinariesKHR; + PFN_vkDestroyPipelineBinaryKHR DestroyPipelineBinaryKHR; + PFN_vkGetPipelineKeyKHR GetPipelineKeyKHR; + PFN_vkGetPipelineBinaryDataKHR GetPipelineBinaryDataKHR; + PFN_vkReleaseCapturedPipelineDataKHR ReleaseCapturedPipelineDataKHR; + + // ---- VK_KHR_line_rasterization extension commands + PFN_vkCmdSetLineStippleKHR CmdSetLineStippleKHR; + // ---- VK_KHR_calibrated_timestamps extension commands PFN_vkGetCalibratedTimestampsKHR GetCalibratedTimestampsKHR; @@ -715,6 +756,7 @@ typedef struct VkLayerDispatchTable_ { // ---- VK_NVX_image_view_handle extension commands PFN_vkGetImageViewHandleNVX GetImageViewHandleNVX; + PFN_vkGetImageViewHandle64NVX GetImageViewHandle64NVX; PFN_vkGetImageViewAddressNVX GetImageViewAddressNVX; // ---- VK_AMD_draw_indirect_count extension commands @@ -832,6 +874,7 @@ typedef struct VkLayerDispatchTable_ { // ---- VK_AMD_buffer_marker extension commands PFN_vkCmdWriteBufferMarkerAMD CmdWriteBufferMarkerAMD; + PFN_vkCmdWriteBufferMarker2AMD CmdWriteBufferMarker2AMD; // ---- VK_EXT_calibrated_timestamps extension commands PFN_vkGetCalibratedTimestampsEXT GetCalibratedTimestampsEXT; @@ -848,6 +891,7 @@ typedef struct VkLayerDispatchTable_ { // ---- VK_NV_device_diagnostic_checkpoints extension commands PFN_vkCmdSetCheckpointNV CmdSetCheckpointNV; PFN_vkGetQueueCheckpointDataNV GetQueueCheckpointDataNV; + PFN_vkGetQueueCheckpointData2NV GetQueueCheckpointData2NV; // ---- VK_INTEL_performance_query extension commands PFN_vkInitializePerformanceApiINTEL InitializePerformanceApiINTEL; @@ -1060,7 +1104,6 @@ typedef struct VkLayerDispatchTable_ { PFN_vkGetPipelineIndirectDeviceAddressNV GetPipelineIndirectDeviceAddressNV; // ---- VK_EXT_extended_dynamic_state3 extension commands - PFN_vkCmdSetTessellationDomainOriginEXT CmdSetTessellationDomainOriginEXT; PFN_vkCmdSetDepthClampEnableEXT CmdSetDepthClampEnableEXT; PFN_vkCmdSetPolygonModeEXT CmdSetPolygonModeEXT; PFN_vkCmdSetRasterizationSamplesEXT CmdSetRasterizationSamplesEXT; @@ -1071,6 +1114,7 @@ typedef struct VkLayerDispatchTable_ { PFN_vkCmdSetColorBlendEnableEXT CmdSetColorBlendEnableEXT; PFN_vkCmdSetColorBlendEquationEXT CmdSetColorBlendEquationEXT; PFN_vkCmdSetColorWriteMaskEXT CmdSetColorWriteMaskEXT; + PFN_vkCmdSetTessellationDomainOriginEXT CmdSetTessellationDomainOriginEXT; PFN_vkCmdSetRasterizationStreamEXT CmdSetRasterizationStreamEXT; PFN_vkCmdSetConservativeRasterizationModeEXT CmdSetConservativeRasterizationModeEXT; PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT CmdSetExtraPrimitiveOverestimationSizeEXT; @@ -1102,16 +1146,24 @@ typedef struct VkLayerDispatchTable_ { PFN_vkBindOpticalFlowSessionImageNV BindOpticalFlowSessionImageNV; PFN_vkCmdOpticalFlowExecuteNV CmdOpticalFlowExecuteNV; + // ---- VK_AMD_anti_lag extension commands + PFN_vkAntiLagUpdateAMD AntiLagUpdateAMD; + // ---- VK_EXT_shader_object extension commands PFN_vkCreateShadersEXT CreateShadersEXT; PFN_vkDestroyShaderEXT DestroyShaderEXT; PFN_vkGetShaderBinaryDataEXT GetShaderBinaryDataEXT; PFN_vkCmdBindShadersEXT CmdBindShadersEXT; + PFN_vkCmdSetDepthClampRangeEXT CmdSetDepthClampRangeEXT; // ---- VK_QCOM_tile_properties extension commands PFN_vkGetFramebufferTilePropertiesQCOM GetFramebufferTilePropertiesQCOM; PFN_vkGetDynamicRenderingTilePropertiesQCOM GetDynamicRenderingTilePropertiesQCOM; + // ---- VK_NV_cooperative_vector extension commands + PFN_vkConvertCooperativeVectorMatrixNV ConvertCooperativeVectorMatrixNV; + PFN_vkCmdConvertCooperativeVectorMatrixNV CmdConvertCooperativeVectorMatrixNV; + // ---- VK_NV_low_latency2 extension commands PFN_vkSetLatencySleepModeNV SetLatencySleepModeNV; PFN_vkLatencySleepNV LatencySleepNV; @@ -1127,6 +1179,33 @@ typedef struct VkLayerDispatchTable_ { PFN_vkGetScreenBufferPropertiesQNX GetScreenBufferPropertiesQNX; #endif // VK_USE_PLATFORM_SCREEN_QNX + // ---- VK_NV_cluster_acceleration_structure extension commands + PFN_vkGetClusterAccelerationStructureBuildSizesNV GetClusterAccelerationStructureBuildSizesNV; + PFN_vkCmdBuildClusterAccelerationStructureIndirectNV CmdBuildClusterAccelerationStructureIndirectNV; + + // ---- VK_NV_partitioned_acceleration_structure extension commands + PFN_vkGetPartitionedAccelerationStructuresBuildSizesNV GetPartitionedAccelerationStructuresBuildSizesNV; + PFN_vkCmdBuildPartitionedAccelerationStructuresNV CmdBuildPartitionedAccelerationStructuresNV; + + // ---- VK_EXT_device_generated_commands extension commands + PFN_vkGetGeneratedCommandsMemoryRequirementsEXT GetGeneratedCommandsMemoryRequirementsEXT; + PFN_vkCmdPreprocessGeneratedCommandsEXT CmdPreprocessGeneratedCommandsEXT; + PFN_vkCmdExecuteGeneratedCommandsEXT CmdExecuteGeneratedCommandsEXT; + PFN_vkCreateIndirectCommandsLayoutEXT CreateIndirectCommandsLayoutEXT; + PFN_vkDestroyIndirectCommandsLayoutEXT DestroyIndirectCommandsLayoutEXT; + PFN_vkCreateIndirectExecutionSetEXT CreateIndirectExecutionSetEXT; + PFN_vkDestroyIndirectExecutionSetEXT DestroyIndirectExecutionSetEXT; + PFN_vkUpdateIndirectExecutionSetPipelineEXT UpdateIndirectExecutionSetPipelineEXT; + PFN_vkUpdateIndirectExecutionSetShaderEXT UpdateIndirectExecutionSetShaderEXT; + + // ---- VK_EXT_external_memory_metal extension commands +#if defined(VK_USE_PLATFORM_METAL_EXT) + PFN_vkGetMemoryMetalHandleEXT GetMemoryMetalHandleEXT; +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_METAL_EXT) + PFN_vkGetMemoryMetalHandlePropertiesEXT GetMemoryMetalHandlePropertiesEXT; +#endif // VK_USE_PLATFORM_METAL_EXT + // ---- VK_KHR_acceleration_structure extension commands PFN_vkCreateAccelerationStructureKHR CreateAccelerationStructureKHR; PFN_vkDestroyAccelerationStructureKHR DestroyAccelerationStructureKHR; diff --git a/loader/generated/vk_loader_extensions.c b/loader/generated/vk_loader_extensions.c index 62e0f7c0298aad203f05673d6cec0c33ae40a286..c919eca23e71eb15065f7a197facdc41239d6b0c 100644 --- a/loader/generated/vk_loader_extensions.c +++ b/loader/generated/vk_loader_extensions.c @@ -28,7 +28,6 @@ #include #include #include -#include "vk_loader_platform.h" #include "loader.h" #include "vk_loader_extensions.h" #include @@ -40,7 +39,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkDevExtError(VkDevice dev) { struct loader_device *found_dev; // The device going in is a trampoline device - struct loader_icd_term *icd_term = loader_get_icd_and_device(dev, &found_dev, NULL); + struct loader_icd_term *icd_term = loader_get_icd_and_device(dev, &found_dev); if (icd_term) loader_log(icd_term->this_instance, VULKAN_LOADER_ERROR_BIT, 0, @@ -318,6 +317,12 @@ VKAPI_ATTR bool VKAPI_CALL loader_icd_init_entries(struct loader_instance* inst, LOOKUP_GIPA(CreateSurfaceOHOS); #endif // VK_USE_PLATFORM_OHOS + // ---- VK_NV_cooperative_vector extension commands + LOOKUP_GIPA(GetPhysicalDeviceCooperativeVectorPropertiesNV); + + // ---- VK_NV_cooperative_matrix2 extension commands + LOOKUP_GIPA(GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV); + #undef LOOKUP_REQUIRED_GIPA #undef LOOKUP_GIPA @@ -524,6 +529,27 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_dispatch_table(struct loader_dev_d table->GetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)gpa(dev, "vkGetDeviceBufferMemoryRequirements"); table->GetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)gpa(dev, "vkGetDeviceImageMemoryRequirements"); table->GetDeviceImageSparseMemoryRequirements = (PFN_vkGetDeviceImageSparseMemoryRequirements)gpa(dev, "vkGetDeviceImageSparseMemoryRequirements"); + + // ---- Core Vulkan 1.4 commands + table->CmdSetLineStipple = (PFN_vkCmdSetLineStipple)gpa(dev, "vkCmdSetLineStipple"); + table->MapMemory2 = (PFN_vkMapMemory2)gpa(dev, "vkMapMemory2"); + table->UnmapMemory2 = (PFN_vkUnmapMemory2)gpa(dev, "vkUnmapMemory2"); + table->CmdBindIndexBuffer2 = (PFN_vkCmdBindIndexBuffer2)gpa(dev, "vkCmdBindIndexBuffer2"); + table->GetRenderingAreaGranularity = (PFN_vkGetRenderingAreaGranularity)gpa(dev, "vkGetRenderingAreaGranularity"); + table->GetDeviceImageSubresourceLayout = (PFN_vkGetDeviceImageSubresourceLayout)gpa(dev, "vkGetDeviceImageSubresourceLayout"); + table->GetImageSubresourceLayout2 = (PFN_vkGetImageSubresourceLayout2)gpa(dev, "vkGetImageSubresourceLayout2"); + table->CmdPushDescriptorSet = (PFN_vkCmdPushDescriptorSet)gpa(dev, "vkCmdPushDescriptorSet"); + table->CmdPushDescriptorSetWithTemplate = (PFN_vkCmdPushDescriptorSetWithTemplate)gpa(dev, "vkCmdPushDescriptorSetWithTemplate"); + table->CmdSetRenderingAttachmentLocations = (PFN_vkCmdSetRenderingAttachmentLocations)gpa(dev, "vkCmdSetRenderingAttachmentLocations"); + table->CmdSetRenderingInputAttachmentIndices = (PFN_vkCmdSetRenderingInputAttachmentIndices)gpa(dev, "vkCmdSetRenderingInputAttachmentIndices"); + table->CmdBindDescriptorSets2 = (PFN_vkCmdBindDescriptorSets2)gpa(dev, "vkCmdBindDescriptorSets2"); + table->CmdPushConstants2 = (PFN_vkCmdPushConstants2)gpa(dev, "vkCmdPushConstants2"); + table->CmdPushDescriptorSet2 = (PFN_vkCmdPushDescriptorSet2)gpa(dev, "vkCmdPushDescriptorSet2"); + table->CmdPushDescriptorSetWithTemplate2 = (PFN_vkCmdPushDescriptorSetWithTemplate2)gpa(dev, "vkCmdPushDescriptorSetWithTemplate2"); + table->CopyMemoryToImage = (PFN_vkCopyMemoryToImage)gpa(dev, "vkCopyMemoryToImage"); + table->CopyImageToMemory = (PFN_vkCopyImageToMemory)gpa(dev, "vkCopyImageToMemory"); + table->CopyImageToImage = (PFN_vkCopyImageToImage)gpa(dev, "vkCopyImageToImage"); + table->TransitionImageLayout = (PFN_vkTransitionImageLayout)gpa(dev, "vkTransitionImageLayout"); } // Init Device function pointer dispatch table with extension commands @@ -661,6 +687,10 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo // ---- VK_KHR_fragment_shading_rate extension commands table->CmdSetFragmentShadingRateKHR = (PFN_vkCmdSetFragmentShadingRateKHR)gdpa(dev, "vkCmdSetFragmentShadingRateKHR"); + // ---- VK_KHR_dynamic_rendering_local_read extension commands + table->CmdSetRenderingAttachmentLocationsKHR = (PFN_vkCmdSetRenderingAttachmentLocationsKHR)gdpa(dev, "vkCmdSetRenderingAttachmentLocationsKHR"); + table->CmdSetRenderingInputAttachmentIndicesKHR = (PFN_vkCmdSetRenderingInputAttachmentIndicesKHR)gdpa(dev, "vkCmdSetRenderingInputAttachmentIndicesKHR"); + // ---- VK_KHR_present_wait extension commands table->WaitForPresentKHR = (PFN_vkWaitForPresentKHR)gdpa(dev, "vkWaitForPresentKHR"); @@ -696,8 +726,6 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo table->CmdPipelineBarrier2KHR = (PFN_vkCmdPipelineBarrier2KHR)gdpa(dev, "vkCmdPipelineBarrier2KHR"); table->CmdWriteTimestamp2KHR = (PFN_vkCmdWriteTimestamp2KHR)gdpa(dev, "vkCmdWriteTimestamp2KHR"); table->QueueSubmit2KHR = (PFN_vkQueueSubmit2KHR)gdpa(dev, "vkQueueSubmit2KHR"); - table->CmdWriteBufferMarker2AMD = (PFN_vkCmdWriteBufferMarker2AMD)gdpa(dev, "vkCmdWriteBufferMarker2AMD"); - table->GetQueueCheckpointData2NV = (PFN_vkGetQueueCheckpointData2NV)gdpa(dev, "vkGetQueueCheckpointData2NV"); // ---- VK_KHR_copy_commands2 extension commands table->CmdCopyBuffer2KHR = (PFN_vkCmdCopyBuffer2KHR)gdpa(dev, "vkCmdCopyBuffer2KHR"); @@ -721,6 +749,16 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo table->GetDeviceImageSubresourceLayoutKHR = (PFN_vkGetDeviceImageSubresourceLayoutKHR)gdpa(dev, "vkGetDeviceImageSubresourceLayoutKHR"); table->GetImageSubresourceLayout2KHR = (PFN_vkGetImageSubresourceLayout2KHR)gdpa(dev, "vkGetImageSubresourceLayout2KHR"); + // ---- VK_KHR_pipeline_binary extension commands + table->CreatePipelineBinariesKHR = (PFN_vkCreatePipelineBinariesKHR)gdpa(dev, "vkCreatePipelineBinariesKHR"); + table->DestroyPipelineBinaryKHR = (PFN_vkDestroyPipelineBinaryKHR)gdpa(dev, "vkDestroyPipelineBinaryKHR"); + table->GetPipelineKeyKHR = (PFN_vkGetPipelineKeyKHR)gdpa(dev, "vkGetPipelineKeyKHR"); + table->GetPipelineBinaryDataKHR = (PFN_vkGetPipelineBinaryDataKHR)gdpa(dev, "vkGetPipelineBinaryDataKHR"); + table->ReleaseCapturedPipelineDataKHR = (PFN_vkReleaseCapturedPipelineDataKHR)gdpa(dev, "vkReleaseCapturedPipelineDataKHR"); + + // ---- VK_KHR_line_rasterization extension commands + table->CmdSetLineStippleKHR = (PFN_vkCmdSetLineStippleKHR)gdpa(dev, "vkCmdSetLineStippleKHR"); + // ---- VK_KHR_calibrated_timestamps extension commands table->GetCalibratedTimestampsKHR = (PFN_vkGetCalibratedTimestampsKHR)gdpa(dev, "vkGetCalibratedTimestampsKHR"); @@ -756,6 +794,7 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo // ---- VK_NVX_image_view_handle extension commands table->GetImageViewHandleNVX = (PFN_vkGetImageViewHandleNVX)gdpa(dev, "vkGetImageViewHandleNVX"); + table->GetImageViewHandle64NVX = (PFN_vkGetImageViewHandle64NVX)gdpa(dev, "vkGetImageViewHandle64NVX"); table->GetImageViewAddressNVX = (PFN_vkGetImageViewAddressNVX)gdpa(dev, "vkGetImageViewAddressNVX"); // ---- VK_AMD_draw_indirect_count extension commands @@ -873,6 +912,7 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo // ---- VK_AMD_buffer_marker extension commands table->CmdWriteBufferMarkerAMD = (PFN_vkCmdWriteBufferMarkerAMD)gdpa(dev, "vkCmdWriteBufferMarkerAMD"); + table->CmdWriteBufferMarker2AMD = (PFN_vkCmdWriteBufferMarker2AMD)gdpa(dev, "vkCmdWriteBufferMarker2AMD"); // ---- VK_EXT_calibrated_timestamps extension commands table->GetCalibratedTimestampsEXT = (PFN_vkGetCalibratedTimestampsEXT)gdpa(dev, "vkGetCalibratedTimestampsEXT"); @@ -889,6 +929,7 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo // ---- VK_NV_device_diagnostic_checkpoints extension commands table->CmdSetCheckpointNV = (PFN_vkCmdSetCheckpointNV)gdpa(dev, "vkCmdSetCheckpointNV"); table->GetQueueCheckpointDataNV = (PFN_vkGetQueueCheckpointDataNV)gdpa(dev, "vkGetQueueCheckpointDataNV"); + table->GetQueueCheckpointData2NV = (PFN_vkGetQueueCheckpointData2NV)gdpa(dev, "vkGetQueueCheckpointData2NV"); // ---- VK_INTEL_performance_query extension commands table->InitializePerformanceApiINTEL = (PFN_vkInitializePerformanceApiINTEL)gdpa(dev, "vkInitializePerformanceApiINTEL"); @@ -1101,7 +1142,6 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo table->GetPipelineIndirectDeviceAddressNV = (PFN_vkGetPipelineIndirectDeviceAddressNV)gdpa(dev, "vkGetPipelineIndirectDeviceAddressNV"); // ---- VK_EXT_extended_dynamic_state3 extension commands - table->CmdSetTessellationDomainOriginEXT = (PFN_vkCmdSetTessellationDomainOriginEXT)gdpa(dev, "vkCmdSetTessellationDomainOriginEXT"); table->CmdSetDepthClampEnableEXT = (PFN_vkCmdSetDepthClampEnableEXT)gdpa(dev, "vkCmdSetDepthClampEnableEXT"); table->CmdSetPolygonModeEXT = (PFN_vkCmdSetPolygonModeEXT)gdpa(dev, "vkCmdSetPolygonModeEXT"); table->CmdSetRasterizationSamplesEXT = (PFN_vkCmdSetRasterizationSamplesEXT)gdpa(dev, "vkCmdSetRasterizationSamplesEXT"); @@ -1112,6 +1152,7 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo table->CmdSetColorBlendEnableEXT = (PFN_vkCmdSetColorBlendEnableEXT)gdpa(dev, "vkCmdSetColorBlendEnableEXT"); table->CmdSetColorBlendEquationEXT = (PFN_vkCmdSetColorBlendEquationEXT)gdpa(dev, "vkCmdSetColorBlendEquationEXT"); table->CmdSetColorWriteMaskEXT = (PFN_vkCmdSetColorWriteMaskEXT)gdpa(dev, "vkCmdSetColorWriteMaskEXT"); + table->CmdSetTessellationDomainOriginEXT = (PFN_vkCmdSetTessellationDomainOriginEXT)gdpa(dev, "vkCmdSetTessellationDomainOriginEXT"); table->CmdSetRasterizationStreamEXT = (PFN_vkCmdSetRasterizationStreamEXT)gdpa(dev, "vkCmdSetRasterizationStreamEXT"); table->CmdSetConservativeRasterizationModeEXT = (PFN_vkCmdSetConservativeRasterizationModeEXT)gdpa(dev, "vkCmdSetConservativeRasterizationModeEXT"); table->CmdSetExtraPrimitiveOverestimationSizeEXT = (PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT)gdpa(dev, "vkCmdSetExtraPrimitiveOverestimationSizeEXT"); @@ -1143,16 +1184,24 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo table->BindOpticalFlowSessionImageNV = (PFN_vkBindOpticalFlowSessionImageNV)gdpa(dev, "vkBindOpticalFlowSessionImageNV"); table->CmdOpticalFlowExecuteNV = (PFN_vkCmdOpticalFlowExecuteNV)gdpa(dev, "vkCmdOpticalFlowExecuteNV"); + // ---- VK_AMD_anti_lag extension commands + table->AntiLagUpdateAMD = (PFN_vkAntiLagUpdateAMD)gdpa(dev, "vkAntiLagUpdateAMD"); + // ---- VK_EXT_shader_object extension commands table->CreateShadersEXT = (PFN_vkCreateShadersEXT)gdpa(dev, "vkCreateShadersEXT"); table->DestroyShaderEXT = (PFN_vkDestroyShaderEXT)gdpa(dev, "vkDestroyShaderEXT"); table->GetShaderBinaryDataEXT = (PFN_vkGetShaderBinaryDataEXT)gdpa(dev, "vkGetShaderBinaryDataEXT"); table->CmdBindShadersEXT = (PFN_vkCmdBindShadersEXT)gdpa(dev, "vkCmdBindShadersEXT"); + table->CmdSetDepthClampRangeEXT = (PFN_vkCmdSetDepthClampRangeEXT)gdpa(dev, "vkCmdSetDepthClampRangeEXT"); // ---- VK_QCOM_tile_properties extension commands table->GetFramebufferTilePropertiesQCOM = (PFN_vkGetFramebufferTilePropertiesQCOM)gdpa(dev, "vkGetFramebufferTilePropertiesQCOM"); table->GetDynamicRenderingTilePropertiesQCOM = (PFN_vkGetDynamicRenderingTilePropertiesQCOM)gdpa(dev, "vkGetDynamicRenderingTilePropertiesQCOM"); + // ---- VK_NV_cooperative_vector extension commands + table->ConvertCooperativeVectorMatrixNV = (PFN_vkConvertCooperativeVectorMatrixNV)gdpa(dev, "vkConvertCooperativeVectorMatrixNV"); + table->CmdConvertCooperativeVectorMatrixNV = (PFN_vkCmdConvertCooperativeVectorMatrixNV)gdpa(dev, "vkCmdConvertCooperativeVectorMatrixNV"); + // ---- VK_NV_low_latency2 extension commands table->SetLatencySleepModeNV = (PFN_vkSetLatencySleepModeNV)gdpa(dev, "vkSetLatencySleepModeNV"); table->LatencySleepNV = (PFN_vkLatencySleepNV)gdpa(dev, "vkLatencySleepNV"); @@ -1168,6 +1217,33 @@ VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct lo table->GetScreenBufferPropertiesQNX = (PFN_vkGetScreenBufferPropertiesQNX)gdpa(dev, "vkGetScreenBufferPropertiesQNX"); #endif // VK_USE_PLATFORM_SCREEN_QNX + // ---- VK_NV_cluster_acceleration_structure extension commands + table->GetClusterAccelerationStructureBuildSizesNV = (PFN_vkGetClusterAccelerationStructureBuildSizesNV)gdpa(dev, "vkGetClusterAccelerationStructureBuildSizesNV"); + table->CmdBuildClusterAccelerationStructureIndirectNV = (PFN_vkCmdBuildClusterAccelerationStructureIndirectNV)gdpa(dev, "vkCmdBuildClusterAccelerationStructureIndirectNV"); + + // ---- VK_NV_partitioned_acceleration_structure extension commands + table->GetPartitionedAccelerationStructuresBuildSizesNV = (PFN_vkGetPartitionedAccelerationStructuresBuildSizesNV)gdpa(dev, "vkGetPartitionedAccelerationStructuresBuildSizesNV"); + table->CmdBuildPartitionedAccelerationStructuresNV = (PFN_vkCmdBuildPartitionedAccelerationStructuresNV)gdpa(dev, "vkCmdBuildPartitionedAccelerationStructuresNV"); + + // ---- VK_EXT_device_generated_commands extension commands + table->GetGeneratedCommandsMemoryRequirementsEXT = (PFN_vkGetGeneratedCommandsMemoryRequirementsEXT)gdpa(dev, "vkGetGeneratedCommandsMemoryRequirementsEXT"); + table->CmdPreprocessGeneratedCommandsEXT = (PFN_vkCmdPreprocessGeneratedCommandsEXT)gdpa(dev, "vkCmdPreprocessGeneratedCommandsEXT"); + table->CmdExecuteGeneratedCommandsEXT = (PFN_vkCmdExecuteGeneratedCommandsEXT)gdpa(dev, "vkCmdExecuteGeneratedCommandsEXT"); + table->CreateIndirectCommandsLayoutEXT = (PFN_vkCreateIndirectCommandsLayoutEXT)gdpa(dev, "vkCreateIndirectCommandsLayoutEXT"); + table->DestroyIndirectCommandsLayoutEXT = (PFN_vkDestroyIndirectCommandsLayoutEXT)gdpa(dev, "vkDestroyIndirectCommandsLayoutEXT"); + table->CreateIndirectExecutionSetEXT = (PFN_vkCreateIndirectExecutionSetEXT)gdpa(dev, "vkCreateIndirectExecutionSetEXT"); + table->DestroyIndirectExecutionSetEXT = (PFN_vkDestroyIndirectExecutionSetEXT)gdpa(dev, "vkDestroyIndirectExecutionSetEXT"); + table->UpdateIndirectExecutionSetPipelineEXT = (PFN_vkUpdateIndirectExecutionSetPipelineEXT)gdpa(dev, "vkUpdateIndirectExecutionSetPipelineEXT"); + table->UpdateIndirectExecutionSetShaderEXT = (PFN_vkUpdateIndirectExecutionSetShaderEXT)gdpa(dev, "vkUpdateIndirectExecutionSetShaderEXT"); + + // ---- VK_EXT_external_memory_metal extension commands +#if defined(VK_USE_PLATFORM_METAL_EXT) + table->GetMemoryMetalHandleEXT = (PFN_vkGetMemoryMetalHandleEXT)gdpa(dev, "vkGetMemoryMetalHandleEXT"); +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_METAL_EXT) + table->GetMemoryMetalHandlePropertiesEXT = (PFN_vkGetMemoryMetalHandlePropertiesEXT)gdpa(dev, "vkGetMemoryMetalHandlePropertiesEXT"); +#endif // VK_USE_PLATFORM_METAL_EXT + // ---- VK_KHR_acceleration_structure extension commands table->CreateAccelerationStructureKHR = (PFN_vkCreateAccelerationStructureKHR)gdpa(dev, "vkCreateAccelerationStructureKHR"); table->DestroyAccelerationStructureKHR = (PFN_vkDestroyAccelerationStructureKHR)gdpa(dev, "vkDestroyAccelerationStructureKHR"); @@ -1479,6 +1555,12 @@ VKAPI_ATTR void VKAPI_CALL loader_init_instance_extension_dispatch_table(VkLayer #if defined(VK_USE_PLATFORM_OHOS) table->CreateSurfaceOHOS = (PFN_vkCreateSurfaceOHOS)gpa(inst, "vkCreateSurfaceOHOS"); #endif // VK_USE_PLATFORM_OHOS + + // ---- VK_NV_cooperative_vector extension commands + table->GetPhysicalDeviceCooperativeVectorPropertiesNV = (PFN_vkGetPhysicalDeviceCooperativeVectorPropertiesNV)gpa(inst, "vkGetPhysicalDeviceCooperativeVectorPropertiesNV"); + + // ---- VK_NV_cooperative_matrix2 extension commands + table->GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV = (PFN_vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV)gpa(inst, "vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV"); } // Functions that required a terminator need to have a separate dispatch table which contains their corresponding @@ -1518,7 +1600,7 @@ void init_extension_device_proc_terminator_dispatch(struct loader_device *dev) { dispatch->CmdInsertDebugUtilsLabelEXT = (PFN_vkCmdInsertDebugUtilsLabelEXT)gpda(dev->icd_device, "vkCmdInsertDebugUtilsLabelEXT"); #if defined(VK_USE_PLATFORM_WIN32_KHR) // ---- VK_EXT_full_screen_exclusive extension commands - if (dev->driver_extensions.ext_full_screen_exclusive_enabled && dev->driver_extensions.khr_device_group_enabled) + if (dev->driver_extensions.ext_full_screen_exclusive_enabled && (dev->driver_extensions.khr_device_group_enabled || dev->driver_extensions.version_1_1_enabled)) dispatch->GetDeviceGroupSurfacePresentModes2EXT = (PFN_vkGetDeviceGroupSurfacePresentModes2EXT)gpda(dev->icd_device, "vkGetDeviceGroupSurfacePresentModes2EXT"); #endif // VK_USE_PLATFORM_WIN32_KHR } @@ -2306,6 +2388,84 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis return (void *)table->GetDeviceImageSparseMemoryRequirements; } + // ---- Core Vulkan 1.4 commands + if (!strcmp(name, "CmdSetLineStipple")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdSetLineStipple; + } + if (!strcmp(name, "MapMemory2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->MapMemory2; + } + if (!strcmp(name, "UnmapMemory2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->UnmapMemory2; + } + if (!strcmp(name, "CmdBindIndexBuffer2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdBindIndexBuffer2; + } + if (!strcmp(name, "GetRenderingAreaGranularity")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->GetRenderingAreaGranularity; + } + if (!strcmp(name, "GetDeviceImageSubresourceLayout")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->GetDeviceImageSubresourceLayout; + } + if (!strcmp(name, "GetImageSubresourceLayout2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->GetImageSubresourceLayout2; + } + if (!strcmp(name, "CmdPushDescriptorSet")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdPushDescriptorSet; + } + if (!strcmp(name, "CmdPushDescriptorSetWithTemplate")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdPushDescriptorSetWithTemplate; + } + if (!strcmp(name, "CmdSetRenderingAttachmentLocations")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdSetRenderingAttachmentLocations; + } + if (!strcmp(name, "CmdSetRenderingInputAttachmentIndices")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdSetRenderingInputAttachmentIndices; + } + if (!strcmp(name, "CmdBindDescriptorSets2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdBindDescriptorSets2; + } + if (!strcmp(name, "CmdPushConstants2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdPushConstants2; + } + if (!strcmp(name, "CmdPushDescriptorSet2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdPushDescriptorSet2; + } + if (!strcmp(name, "CmdPushDescriptorSetWithTemplate2")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CmdPushDescriptorSetWithTemplate2; + } + if (!strcmp(name, "CopyMemoryToImage")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CopyMemoryToImage; + } + if (!strcmp(name, "CopyImageToMemory")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CopyImageToMemory; + } + if (!strcmp(name, "CopyImageToImage")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->CopyImageToImage; + } + if (!strcmp(name, "TransitionImageLayout")) { + if (dev->should_ignore_device_commands_from_newer_version && api_version < VK_API_VERSION_1_4) return NULL; + return (void *)table->TransitionImageLayout; + } + // ---- VK_KHR_swapchain extension commands if (!strcmp(name, "CreateSwapchainKHR")) return (void *)table->CreateSwapchainKHR; if (!strcmp(name, "DestroySwapchainKHR")) return (void *)table->DestroySwapchainKHR; @@ -2432,6 +2592,10 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis // ---- VK_KHR_fragment_shading_rate extension commands if (!strcmp(name, "CmdSetFragmentShadingRateKHR")) return (void *)table->CmdSetFragmentShadingRateKHR; + // ---- VK_KHR_dynamic_rendering_local_read extension commands + if (!strcmp(name, "CmdSetRenderingAttachmentLocationsKHR")) return (void *)table->CmdSetRenderingAttachmentLocationsKHR; + if (!strcmp(name, "CmdSetRenderingInputAttachmentIndicesKHR")) return (void *)table->CmdSetRenderingInputAttachmentIndicesKHR; + // ---- VK_KHR_present_wait extension commands if (!strcmp(name, "WaitForPresentKHR")) return (void *)table->WaitForPresentKHR; @@ -2467,8 +2631,6 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis if (!strcmp(name, "CmdPipelineBarrier2KHR")) return (void *)table->CmdPipelineBarrier2KHR; if (!strcmp(name, "CmdWriteTimestamp2KHR")) return (void *)table->CmdWriteTimestamp2KHR; if (!strcmp(name, "QueueSubmit2KHR")) return (void *)table->QueueSubmit2KHR; - if (!strcmp(name, "CmdWriteBufferMarker2AMD")) return (void *)table->CmdWriteBufferMarker2AMD; - if (!strcmp(name, "GetQueueCheckpointData2NV")) return (void *)table->GetQueueCheckpointData2NV; // ---- VK_KHR_copy_commands2 extension commands if (!strcmp(name, "CmdCopyBuffer2KHR")) return (void *)table->CmdCopyBuffer2KHR; @@ -2492,6 +2654,16 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis if (!strcmp(name, "GetDeviceImageSubresourceLayoutKHR")) return (void *)table->GetDeviceImageSubresourceLayoutKHR; if (!strcmp(name, "GetImageSubresourceLayout2KHR")) return (void *)table->GetImageSubresourceLayout2KHR; + // ---- VK_KHR_pipeline_binary extension commands + if (!strcmp(name, "CreatePipelineBinariesKHR")) return (void *)table->CreatePipelineBinariesKHR; + if (!strcmp(name, "DestroyPipelineBinaryKHR")) return (void *)table->DestroyPipelineBinaryKHR; + if (!strcmp(name, "GetPipelineKeyKHR")) return (void *)table->GetPipelineKeyKHR; + if (!strcmp(name, "GetPipelineBinaryDataKHR")) return (void *)table->GetPipelineBinaryDataKHR; + if (!strcmp(name, "ReleaseCapturedPipelineDataKHR")) return (void *)table->ReleaseCapturedPipelineDataKHR; + + // ---- VK_KHR_line_rasterization extension commands + if (!strcmp(name, "CmdSetLineStippleKHR")) return (void *)table->CmdSetLineStippleKHR; + // ---- VK_KHR_calibrated_timestamps extension commands if (!strcmp(name, "GetCalibratedTimestampsKHR")) return (void *)table->GetCalibratedTimestampsKHR; @@ -2527,6 +2699,7 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis // ---- VK_NVX_image_view_handle extension commands if (!strcmp(name, "GetImageViewHandleNVX")) return (void *)table->GetImageViewHandleNVX; + if (!strcmp(name, "GetImageViewHandle64NVX")) return (void *)table->GetImageViewHandle64NVX; if (!strcmp(name, "GetImageViewAddressNVX")) return (void *)table->GetImageViewAddressNVX; // ---- VK_AMD_draw_indirect_count extension commands @@ -2644,6 +2817,7 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis // ---- VK_AMD_buffer_marker extension commands if (!strcmp(name, "CmdWriteBufferMarkerAMD")) return (void *)table->CmdWriteBufferMarkerAMD; + if (!strcmp(name, "CmdWriteBufferMarker2AMD")) return (void *)table->CmdWriteBufferMarker2AMD; // ---- VK_EXT_calibrated_timestamps extension commands if (!strcmp(name, "GetCalibratedTimestampsEXT")) return (void *)table->GetCalibratedTimestampsEXT; @@ -2660,6 +2834,7 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis // ---- VK_NV_device_diagnostic_checkpoints extension commands if (!strcmp(name, "CmdSetCheckpointNV")) return (void *)table->CmdSetCheckpointNV; if (!strcmp(name, "GetQueueCheckpointDataNV")) return (void *)table->GetQueueCheckpointDataNV; + if (!strcmp(name, "GetQueueCheckpointData2NV")) return (void *)table->GetQueueCheckpointData2NV; // ---- VK_INTEL_performance_query extension commands if (!strcmp(name, "InitializePerformanceApiINTEL")) return (void *)table->InitializePerformanceApiINTEL; @@ -2872,7 +3047,6 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis if (!strcmp(name, "GetPipelineIndirectDeviceAddressNV")) return (void *)table->GetPipelineIndirectDeviceAddressNV; // ---- VK_EXT_extended_dynamic_state3 extension commands - if (!strcmp(name, "CmdSetTessellationDomainOriginEXT")) return (void *)table->CmdSetTessellationDomainOriginEXT; if (!strcmp(name, "CmdSetDepthClampEnableEXT")) return (void *)table->CmdSetDepthClampEnableEXT; if (!strcmp(name, "CmdSetPolygonModeEXT")) return (void *)table->CmdSetPolygonModeEXT; if (!strcmp(name, "CmdSetRasterizationSamplesEXT")) return (void *)table->CmdSetRasterizationSamplesEXT; @@ -2883,6 +3057,7 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis if (!strcmp(name, "CmdSetColorBlendEnableEXT")) return (void *)table->CmdSetColorBlendEnableEXT; if (!strcmp(name, "CmdSetColorBlendEquationEXT")) return (void *)table->CmdSetColorBlendEquationEXT; if (!strcmp(name, "CmdSetColorWriteMaskEXT")) return (void *)table->CmdSetColorWriteMaskEXT; + if (!strcmp(name, "CmdSetTessellationDomainOriginEXT")) return (void *)table->CmdSetTessellationDomainOriginEXT; if (!strcmp(name, "CmdSetRasterizationStreamEXT")) return (void *)table->CmdSetRasterizationStreamEXT; if (!strcmp(name, "CmdSetConservativeRasterizationModeEXT")) return (void *)table->CmdSetConservativeRasterizationModeEXT; if (!strcmp(name, "CmdSetExtraPrimitiveOverestimationSizeEXT")) return (void *)table->CmdSetExtraPrimitiveOverestimationSizeEXT; @@ -2914,16 +3089,24 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis if (!strcmp(name, "BindOpticalFlowSessionImageNV")) return (void *)table->BindOpticalFlowSessionImageNV; if (!strcmp(name, "CmdOpticalFlowExecuteNV")) return (void *)table->CmdOpticalFlowExecuteNV; + // ---- VK_AMD_anti_lag extension commands + if (!strcmp(name, "AntiLagUpdateAMD")) return (void *)table->AntiLagUpdateAMD; + // ---- VK_EXT_shader_object extension commands if (!strcmp(name, "CreateShadersEXT")) return (void *)table->CreateShadersEXT; if (!strcmp(name, "DestroyShaderEXT")) return (void *)table->DestroyShaderEXT; if (!strcmp(name, "GetShaderBinaryDataEXT")) return (void *)table->GetShaderBinaryDataEXT; if (!strcmp(name, "CmdBindShadersEXT")) return (void *)table->CmdBindShadersEXT; + if (!strcmp(name, "CmdSetDepthClampRangeEXT")) return (void *)table->CmdSetDepthClampRangeEXT; // ---- VK_QCOM_tile_properties extension commands if (!strcmp(name, "GetFramebufferTilePropertiesQCOM")) return (void *)table->GetFramebufferTilePropertiesQCOM; if (!strcmp(name, "GetDynamicRenderingTilePropertiesQCOM")) return (void *)table->GetDynamicRenderingTilePropertiesQCOM; + // ---- VK_NV_cooperative_vector extension commands + if (!strcmp(name, "ConvertCooperativeVectorMatrixNV")) return (void *)table->ConvertCooperativeVectorMatrixNV; + if (!strcmp(name, "CmdConvertCooperativeVectorMatrixNV")) return (void *)table->CmdConvertCooperativeVectorMatrixNV; + // ---- VK_NV_low_latency2 extension commands if (!strcmp(name, "SetLatencySleepModeNV")) return (void *)table->SetLatencySleepModeNV; if (!strcmp(name, "LatencySleepNV")) return (void *)table->LatencySleepNV; @@ -2939,6 +3122,33 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDis if (!strcmp(name, "GetScreenBufferPropertiesQNX")) return (void *)table->GetScreenBufferPropertiesQNX; #endif // VK_USE_PLATFORM_SCREEN_QNX + // ---- VK_NV_cluster_acceleration_structure extension commands + if (!strcmp(name, "GetClusterAccelerationStructureBuildSizesNV")) return (void *)table->GetClusterAccelerationStructureBuildSizesNV; + if (!strcmp(name, "CmdBuildClusterAccelerationStructureIndirectNV")) return (void *)table->CmdBuildClusterAccelerationStructureIndirectNV; + + // ---- VK_NV_partitioned_acceleration_structure extension commands + if (!strcmp(name, "GetPartitionedAccelerationStructuresBuildSizesNV")) return (void *)table->GetPartitionedAccelerationStructuresBuildSizesNV; + if (!strcmp(name, "CmdBuildPartitionedAccelerationStructuresNV")) return (void *)table->CmdBuildPartitionedAccelerationStructuresNV; + + // ---- VK_EXT_device_generated_commands extension commands + if (!strcmp(name, "GetGeneratedCommandsMemoryRequirementsEXT")) return (void *)table->GetGeneratedCommandsMemoryRequirementsEXT; + if (!strcmp(name, "CmdPreprocessGeneratedCommandsEXT")) return (void *)table->CmdPreprocessGeneratedCommandsEXT; + if (!strcmp(name, "CmdExecuteGeneratedCommandsEXT")) return (void *)table->CmdExecuteGeneratedCommandsEXT; + if (!strcmp(name, "CreateIndirectCommandsLayoutEXT")) return (void *)table->CreateIndirectCommandsLayoutEXT; + if (!strcmp(name, "DestroyIndirectCommandsLayoutEXT")) return (void *)table->DestroyIndirectCommandsLayoutEXT; + if (!strcmp(name, "CreateIndirectExecutionSetEXT")) return (void *)table->CreateIndirectExecutionSetEXT; + if (!strcmp(name, "DestroyIndirectExecutionSetEXT")) return (void *)table->DestroyIndirectExecutionSetEXT; + if (!strcmp(name, "UpdateIndirectExecutionSetPipelineEXT")) return (void *)table->UpdateIndirectExecutionSetPipelineEXT; + if (!strcmp(name, "UpdateIndirectExecutionSetShaderEXT")) return (void *)table->UpdateIndirectExecutionSetShaderEXT; + + // ---- VK_EXT_external_memory_metal extension commands +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (!strcmp(name, "GetMemoryMetalHandleEXT")) return (void *)table->GetMemoryMetalHandleEXT; +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (!strcmp(name, "GetMemoryMetalHandlePropertiesEXT")) return (void *)table->GetMemoryMetalHandlePropertiesEXT; +#endif // VK_USE_PLATFORM_METAL_EXT + // ---- VK_KHR_acceleration_structure extension commands if (!strcmp(name, "CreateAccelerationStructureKHR")) return (void *)table->CreateAccelerationStructureKHR; if (!strcmp(name, "DestroyAccelerationStructureKHR")) return (void *)table->DestroyAccelerationStructureKHR; @@ -3257,6 +3467,12 @@ VKAPI_ATTR void* VKAPI_CALL loader_lookup_instance_dispatch_table(const VkLayerI if (!strcmp(name, "CreateSurfaceOHOS")) return (void *)table->CreateSurfaceOHOS; #endif // VK_USE_PLATFORM_OHOS + // ---- VK_NV_cooperative_vector extension commands + if (!strcmp(name, "GetPhysicalDeviceCooperativeVectorPropertiesNV")) return (void *)table->GetPhysicalDeviceCooperativeVectorPropertiesNV; + + // ---- VK_NV_cooperative_matrix2 extension commands + if (!strcmp(name, "GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV")) return (void *)table->GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV; + *found_name = false; return NULL; } @@ -4281,6 +4497,35 @@ VKAPI_ATTR void VKAPI_CALL CmdSetFragmentShadingRateKHR( } +// ---- VK_KHR_dynamic_rendering_local_read extension trampoline/terminators + +VKAPI_ATTR void VKAPI_CALL CmdSetRenderingAttachmentLocationsKHR( + VkCommandBuffer commandBuffer, + const VkRenderingAttachmentLocationInfo* pLocationInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetRenderingAttachmentLocationsKHR: Invalid commandBuffer " + "[VUID-vkCmdSetRenderingAttachmentLocationsKHR-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetRenderingAttachmentLocationsKHR(commandBuffer, pLocationInfo); +} + +VKAPI_ATTR void VKAPI_CALL CmdSetRenderingInputAttachmentIndicesKHR( + VkCommandBuffer commandBuffer, + const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetRenderingInputAttachmentIndicesKHR: Invalid commandBuffer " + "[VUID-vkCmdSetRenderingInputAttachmentIndicesKHR-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetRenderingInputAttachmentIndicesKHR(commandBuffer, pInputAttachmentIndexInfo); +} + + // ---- VK_KHR_present_wait extension trampoline/terminators VKAPI_ATTR VkResult VKAPI_CALL WaitForPresentKHR( @@ -4463,7 +4708,7 @@ VKAPI_ATTR VkResult VKAPI_CALL GetPipelineExecutableInternalRepresentationsKHR( VKAPI_ATTR VkResult VKAPI_CALL MapMemory2KHR( VkDevice device, - const VkMemoryMapInfoKHR* pMemoryMapInfo, + const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { @@ -4477,7 +4722,7 @@ VKAPI_ATTR VkResult VKAPI_CALL MapMemory2KHR( VKAPI_ATTR VkResult VKAPI_CALL UnmapMemory2KHR( VkDevice device, - const VkMemoryUnmapInfoKHR* pMemoryUnmapInfo) { + const VkMemoryUnmapInfo* pMemoryUnmapInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -4639,36 +4884,6 @@ VKAPI_ATTR VkResult VKAPI_CALL QueueSubmit2KHR( return disp->QueueSubmit2KHR(queue, submitCount, pSubmits, fence); } -VKAPI_ATTR void VKAPI_CALL CmdWriteBufferMarker2AMD( - VkCommandBuffer commandBuffer, - VkPipelineStageFlags2 stage, - VkBuffer dstBuffer, - VkDeviceSize dstOffset, - uint32_t marker) { - const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); - if (NULL == disp) { - loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, - "vkCmdWriteBufferMarker2AMD: Invalid commandBuffer " - "[VUID-vkCmdWriteBufferMarker2AMD-commandBuffer-parameter]"); - abort(); /* Intentionally fail so user can correct issue. */ - } - disp->CmdWriteBufferMarker2AMD(commandBuffer, stage, dstBuffer, dstOffset, marker); -} - -VKAPI_ATTR void VKAPI_CALL GetQueueCheckpointData2NV( - VkQueue queue, - uint32_t* pCheckpointDataCount, - VkCheckpointData2NV* pCheckpointData) { - const VkLayerDispatchTable *disp = loader_get_dispatch(queue); - if (NULL == disp) { - loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, - "vkGetQueueCheckpointData2NV: Invalid queue " - "[VUID-vkGetQueueCheckpointData2NV-queue-parameter]"); - abort(); /* Intentionally fail so user can correct issue. */ - } - disp->GetQueueCheckpointData2NV(queue, pCheckpointDataCount, pCheckpointData); -} - // ---- VK_KHR_copy_commands2 extension trampoline/terminators @@ -4833,7 +5048,7 @@ VKAPI_ATTR void VKAPI_CALL CmdBindIndexBuffer2KHR( VKAPI_ATTR void VKAPI_CALL GetRenderingAreaGranularityKHR( VkDevice device, - const VkRenderingAreaInfoKHR* pRenderingAreaInfo, + const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { @@ -4847,8 +5062,8 @@ VKAPI_ATTR void VKAPI_CALL GetRenderingAreaGranularityKHR( VKAPI_ATTR void VKAPI_CALL GetDeviceImageSubresourceLayoutKHR( VkDevice device, - const VkDeviceImageSubresourceInfoKHR* pInfo, - VkSubresourceLayout2KHR* pLayout) { + const VkDeviceImageSubresourceInfo* pInfo, + VkSubresourceLayout2* pLayout) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -4862,8 +5077,8 @@ VKAPI_ATTR void VKAPI_CALL GetDeviceImageSubresourceLayoutKHR( VKAPI_ATTR void VKAPI_CALL GetImageSubresourceLayout2KHR( VkDevice device, VkImage image, - const VkImageSubresource2KHR* pSubresource, - VkSubresourceLayout2KHR* pLayout) { + const VkImageSubresource2* pSubresource, + VkSubresourceLayout2* pLayout) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -4875,6 +5090,82 @@ VKAPI_ATTR void VKAPI_CALL GetImageSubresourceLayout2KHR( } +// ---- VK_KHR_pipeline_binary extension trampoline/terminators + +VKAPI_ATTR VkResult VKAPI_CALL CreatePipelineBinariesKHR( + VkDevice device, + const VkPipelineBinaryCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineBinaryHandlesInfoKHR* pBinaries) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCreatePipelineBinariesKHR: Invalid device " + "[VUID-vkCreatePipelineBinariesKHR-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->CreatePipelineBinariesKHR(device, pCreateInfo, pAllocator, pBinaries); +} + +VKAPI_ATTR void VKAPI_CALL DestroyPipelineBinaryKHR( + VkDevice device, + VkPipelineBinaryKHR pipelineBinary, + const VkAllocationCallbacks* pAllocator) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkDestroyPipelineBinaryKHR: Invalid device " + "[VUID-vkDestroyPipelineBinaryKHR-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->DestroyPipelineBinaryKHR(device, pipelineBinary, pAllocator); +} + +VKAPI_ATTR VkResult VKAPI_CALL GetPipelineKeyKHR( + VkDevice device, + const VkPipelineCreateInfoKHR* pPipelineCreateInfo, + VkPipelineBinaryKeyKHR* pPipelineKey) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPipelineKeyKHR: Invalid device " + "[VUID-vkGetPipelineKeyKHR-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->GetPipelineKeyKHR(device, pPipelineCreateInfo, pPipelineKey); +} + +VKAPI_ATTR VkResult VKAPI_CALL GetPipelineBinaryDataKHR( + VkDevice device, + const VkPipelineBinaryDataInfoKHR* pInfo, + VkPipelineBinaryKeyKHR* pPipelineBinaryKey, + size_t* pPipelineBinaryDataSize, + void* pPipelineBinaryData) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPipelineBinaryDataKHR: Invalid device " + "[VUID-vkGetPipelineBinaryDataKHR-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->GetPipelineBinaryDataKHR(device, pInfo, pPipelineBinaryKey, pPipelineBinaryDataSize, pPipelineBinaryData); +} + +VKAPI_ATTR VkResult VKAPI_CALL ReleaseCapturedPipelineDataKHR( + VkDevice device, + const VkReleaseCapturedPipelineDataInfoKHR* pInfo, + const VkAllocationCallbacks* pAllocator) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkReleaseCapturedPipelineDataKHR: Invalid device " + "[VUID-vkReleaseCapturedPipelineDataKHR-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->ReleaseCapturedPipelineDataKHR(device, pInfo, pAllocator); +} + + // ---- VK_KHR_cooperative_matrix extension trampoline/terminators VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceCooperativeMatrixPropertiesKHR( @@ -4908,6 +5199,23 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceCooperativeMatrixProp } +// ---- VK_KHR_line_rasterization extension trampoline/terminators + +VKAPI_ATTR void VKAPI_CALL CmdSetLineStippleKHR( + VkCommandBuffer commandBuffer, + uint32_t lineStippleFactor, + uint16_t lineStipplePattern) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetLineStippleKHR: Invalid commandBuffer " + "[VUID-vkCmdSetLineStippleKHR-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetLineStippleKHR(commandBuffer, lineStippleFactor, lineStipplePattern); +} + + // ---- VK_KHR_calibrated_timestamps extension trampoline/terminators VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceCalibrateableTimeDomainsKHR( @@ -4961,7 +5269,7 @@ VKAPI_ATTR VkResult VKAPI_CALL GetCalibratedTimestampsKHR( VKAPI_ATTR void VKAPI_CALL CmdBindDescriptorSets2KHR( VkCommandBuffer commandBuffer, - const VkBindDescriptorSetsInfoKHR* pBindDescriptorSetsInfo) { + const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -4974,7 +5282,7 @@ VKAPI_ATTR void VKAPI_CALL CmdBindDescriptorSets2KHR( VKAPI_ATTR void VKAPI_CALL CmdPushConstants2KHR( VkCommandBuffer commandBuffer, - const VkPushConstantsInfoKHR* pPushConstantsInfo) { + const VkPushConstantsInfo* pPushConstantsInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -4987,7 +5295,7 @@ VKAPI_ATTR void VKAPI_CALL CmdPushConstants2KHR( VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSet2KHR( VkCommandBuffer commandBuffer, - const VkPushDescriptorSetInfoKHR* pPushDescriptorSetInfo) { + const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -5000,7 +5308,7 @@ VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSet2KHR( VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSetWithTemplate2KHR( VkCommandBuffer commandBuffer, - const VkPushDescriptorSetWithTemplateInfoKHR* pPushDescriptorSetWithTemplateInfo) { + const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -5067,9 +5375,8 @@ VKAPI_ATTR VkResult VKAPI_CALL DebugMarkerSetObjectTagEXT( VKAPI_ATTR VkResult VKAPI_CALL terminator_DebugMarkerSetObjectTagEXT( VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "DebugMarkerSetObjectTagEXT: Invalid device handle"); abort(); /* Intentionally fail so user can correct issue. */ @@ -5084,8 +5391,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_DebugMarkerSetObjectTagEXT( } else if (pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) { if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) { VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pTagInfo->object; - if (NULL != icd_surface->real_icd_surfaces) { - local_tag_info.object = (uint64_t)icd_surface->real_icd_surfaces[icd_index]; + if (NULL != icd_term->surface_list.list && icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) + && icd_term->surface_list.list[icd_surface->surface_index]) { + local_tag_info.object = (uint64_t)icd_term->surface_list.list[icd_surface->surface_index]; } } // If this is an instance we have to replace it with the proper one for the next call. @@ -5126,9 +5434,8 @@ VKAPI_ATTR VkResult VKAPI_CALL DebugMarkerSetObjectNameEXT( VKAPI_ATTR VkResult VKAPI_CALL terminator_DebugMarkerSetObjectNameEXT( VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "DebugMarkerSetObjectNameEXT: Invalid device handle"); abort(); /* Intentionally fail so user can correct issue. */ @@ -5143,8 +5450,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_DebugMarkerSetObjectNameEXT( } else if (pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) { if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) { VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pNameInfo->object; - if (NULL != icd_surface->real_icd_surfaces) { - local_name_info.object = (uint64_t)icd_surface->real_icd_surfaces[icd_index]; + if (NULL != icd_term->surface_list.list && icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) + && icd_term->surface_list.list[icd_surface->surface_index]) { + local_name_info.object = (uint64_t)icd_term->surface_list.list[icd_surface->surface_index]; } } // If this is an instance we have to replace it with the proper one for the next call. @@ -5387,6 +5695,19 @@ VKAPI_ATTR uint32_t VKAPI_CALL GetImageViewHandleNVX( return disp->GetImageViewHandleNVX(device, pInfo); } +VKAPI_ATTR uint64_t VKAPI_CALL GetImageViewHandle64NVX( + VkDevice device, + const VkImageViewHandleInfoNVX* pInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetImageViewHandle64NVX: Invalid device " + "[VUID-vkGetImageViewHandle64NVX-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->GetImageViewHandle64NVX(device, pInfo); +} + VKAPI_ATTR VkResult VKAPI_CALL GetImageViewAddressNVX( VkDevice device, VkImageView imageView, @@ -5717,9 +6038,8 @@ VKAPI_ATTR VkResult VKAPI_CALL SetDebugUtilsObjectNameEXT( VKAPI_ATTR VkResult VKAPI_CALL terminator_SetDebugUtilsObjectNameEXT( VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "SetDebugUtilsObjectNameEXT: Invalid device handle"); abort(); /* Intentionally fail so user can correct issue. */ @@ -5734,8 +6054,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_SetDebugUtilsObjectNameEXT( } else if (pNameInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) { if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) { VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pNameInfo->objectHandle; - if (NULL != icd_surface->real_icd_surfaces) { - local_name_info.objectHandle = (uint64_t)icd_surface->real_icd_surfaces[icd_index]; + if (NULL != icd_term->surface_list.list && icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) + && icd_term->surface_list.list[icd_surface->surface_index]) { + local_name_info.objectHandle = (uint64_t)icd_term->surface_list.list[icd_surface->surface_index]; } } // If this is an instance we have to replace it with the proper one for the next call. @@ -5780,9 +6101,8 @@ VKAPI_ATTR VkResult VKAPI_CALL SetDebugUtilsObjectTagEXT( VKAPI_ATTR VkResult VKAPI_CALL terminator_SetDebugUtilsObjectTagEXT( VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "SetDebugUtilsObjectTagEXT: Invalid device handle"); abort(); /* Intentionally fail so user can correct issue. */ @@ -5797,8 +6117,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_SetDebugUtilsObjectTagEXT( } else if (pTagInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) { if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) { VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pTagInfo->objectHandle; - if (NULL != icd_surface->real_icd_surfaces) { - local_tag_info.objectHandle = (uint64_t)icd_surface->real_icd_surfaces[icd_index]; + if (NULL != icd_term->surface_list.list && icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) + && icd_term->surface_list.list[icd_surface->surface_index]) { + local_tag_info.objectHandle = (uint64_t)icd_term->surface_list.list[icd_surface->surface_index]; } } // If this is an instance we have to replace it with the proper one for the next call. @@ -6069,7 +6390,9 @@ VKAPI_ATTR VkResult VKAPI_CALL GetExecutionGraphPipelineNodeIndexAMDX( #if defined(VK_ENABLE_BETA_EXTENSIONS) VKAPI_ATTR void VKAPI_CALL CmdInitializeGraphScratchMemoryAMDX( VkCommandBuffer commandBuffer, - VkDeviceAddress scratch) { + VkPipeline executionGraph, + VkDeviceAddress scratch, + VkDeviceSize scratchSize) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -6077,7 +6400,7 @@ VKAPI_ATTR void VKAPI_CALL CmdInitializeGraphScratchMemoryAMDX( "[VUID-vkCmdInitializeGraphScratchMemoryAMDX-commandBuffer-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } - disp->CmdInitializeGraphScratchMemoryAMDX(commandBuffer, scratch); + disp->CmdInitializeGraphScratchMemoryAMDX(commandBuffer, executionGraph, scratch, scratchSize); } #endif // VK_ENABLE_BETA_EXTENSIONS @@ -6085,6 +6408,7 @@ VKAPI_ATTR void VKAPI_CALL CmdInitializeGraphScratchMemoryAMDX( VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphAMDX( VkCommandBuffer commandBuffer, VkDeviceAddress scratch, + VkDeviceSize scratchSize, const VkDispatchGraphCountInfoAMDX* pCountInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { @@ -6093,7 +6417,7 @@ VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphAMDX( "[VUID-vkCmdDispatchGraphAMDX-commandBuffer-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } - disp->CmdDispatchGraphAMDX(commandBuffer, scratch, pCountInfo); + disp->CmdDispatchGraphAMDX(commandBuffer, scratch, scratchSize, pCountInfo); } #endif // VK_ENABLE_BETA_EXTENSIONS @@ -6101,6 +6425,7 @@ VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphAMDX( VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphIndirectAMDX( VkCommandBuffer commandBuffer, VkDeviceAddress scratch, + VkDeviceSize scratchSize, const VkDispatchGraphCountInfoAMDX* pCountInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { @@ -6109,7 +6434,7 @@ VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphIndirectAMDX( "[VUID-vkCmdDispatchGraphIndirectAMDX-commandBuffer-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } - disp->CmdDispatchGraphIndirectAMDX(commandBuffer, scratch, pCountInfo); + disp->CmdDispatchGraphIndirectAMDX(commandBuffer, scratch, scratchSize, pCountInfo); } #endif // VK_ENABLE_BETA_EXTENSIONS @@ -6117,6 +6442,7 @@ VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphIndirectAMDX( VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphIndirectCountAMDX( VkCommandBuffer commandBuffer, VkDeviceAddress scratch, + VkDeviceSize scratchSize, VkDeviceAddress countInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); if (NULL == disp) { @@ -6125,7 +6451,7 @@ VKAPI_ATTR void VKAPI_CALL CmdDispatchGraphIndirectCountAMDX( "[VUID-vkCmdDispatchGraphIndirectCountAMDX-commandBuffer-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } - disp->CmdDispatchGraphIndirectCountAMDX(commandBuffer, scratch, countInfo); + disp->CmdDispatchGraphIndirectCountAMDX(commandBuffer, scratch, scratchSize, countInfo); } #endif // VK_ENABLE_BETA_EXTENSIONS @@ -6556,6 +6882,22 @@ VKAPI_ATTR void VKAPI_CALL CmdWriteBufferMarkerAMD( disp->CmdWriteBufferMarkerAMD(commandBuffer, pipelineStage, dstBuffer, dstOffset, marker); } +VKAPI_ATTR void VKAPI_CALL CmdWriteBufferMarker2AMD( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags2 stage, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + uint32_t marker) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdWriteBufferMarker2AMD: Invalid commandBuffer " + "[VUID-vkCmdWriteBufferMarker2AMD-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdWriteBufferMarker2AMD(commandBuffer, stage, dstBuffer, dstOffset, marker); +} + // ---- VK_EXT_calibrated_timestamps extension trampoline/terminators @@ -6719,6 +7061,20 @@ VKAPI_ATTR void VKAPI_CALL GetQueueCheckpointDataNV( disp->GetQueueCheckpointDataNV(queue, pCheckpointDataCount, pCheckpointData); } +VKAPI_ATTR void VKAPI_CALL GetQueueCheckpointData2NV( + VkQueue queue, + uint32_t* pCheckpointDataCount, + VkCheckpointData2NV* pCheckpointData) { + const VkLayerDispatchTable *disp = loader_get_dispatch(queue); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetQueueCheckpointData2NV: Invalid queue " + "[VUID-vkGetQueueCheckpointData2NV-queue-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetQueueCheckpointData2NV(queue, pCheckpointDataCount, pCheckpointData); +} + // ---- VK_INTEL_performance_query extension trampoline/terminators @@ -7182,7 +7538,7 @@ VKAPI_ATTR void VKAPI_CALL CmdSetStencilOpEXT( VKAPI_ATTR VkResult VKAPI_CALL CopyMemoryToImageEXT( VkDevice device, - const VkCopyMemoryToImageInfoEXT* pCopyMemoryToImageInfo) { + const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -7195,7 +7551,7 @@ VKAPI_ATTR VkResult VKAPI_CALL CopyMemoryToImageEXT( VKAPI_ATTR VkResult VKAPI_CALL CopyImageToMemoryEXT( VkDevice device, - const VkCopyImageToMemoryInfoEXT* pCopyImageToMemoryInfo) { + const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -7208,7 +7564,7 @@ VKAPI_ATTR VkResult VKAPI_CALL CopyImageToMemoryEXT( VKAPI_ATTR VkResult VKAPI_CALL CopyImageToImageEXT( VkDevice device, - const VkCopyImageToImageInfoEXT* pCopyImageToImageInfo) { + const VkCopyImageToImageInfo* pCopyImageToImageInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -7222,7 +7578,7 @@ VKAPI_ATTR VkResult VKAPI_CALL CopyImageToImageEXT( VKAPI_ATTR VkResult VKAPI_CALL TransitionImageLayoutEXT( VkDevice device, uint32_t transitionCount, - const VkHostImageLayoutTransitionInfoEXT* pTransitions) { + const VkHostImageLayoutTransitionInfo* pTransitions) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -7236,8 +7592,8 @@ VKAPI_ATTR VkResult VKAPI_CALL TransitionImageLayoutEXT( VKAPI_ATTR void VKAPI_CALL GetImageSubresourceLayout2EXT( VkDevice device, VkImage image, - const VkImageSubresource2KHR* pSubresource, - VkSubresourceLayout2KHR* pLayout) { + const VkImageSubresource2* pSubresource, + VkSubresourceLayout2* pLayout) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, @@ -8644,19 +9000,6 @@ VKAPI_ATTR VkDeviceAddress VKAPI_CALL GetPipelineIndirectDeviceAddressNV( // ---- VK_EXT_extended_dynamic_state3 extension trampoline/terminators -VKAPI_ATTR void VKAPI_CALL CmdSetTessellationDomainOriginEXT( - VkCommandBuffer commandBuffer, - VkTessellationDomainOrigin domainOrigin) { - const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); - if (NULL == disp) { - loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, - "vkCmdSetTessellationDomainOriginEXT: Invalid commandBuffer " - "[VUID-vkCmdSetTessellationDomainOriginEXT-commandBuffer-parameter]"); - abort(); /* Intentionally fail so user can correct issue. */ - } - disp->CmdSetTessellationDomainOriginEXT(commandBuffer, domainOrigin); -} - VKAPI_ATTR void VKAPI_CALL CmdSetDepthClampEnableEXT( VkCommandBuffer commandBuffer, VkBool32 depthClampEnable) { @@ -8794,6 +9137,19 @@ VKAPI_ATTR void VKAPI_CALL CmdSetColorWriteMaskEXT( disp->CmdSetColorWriteMaskEXT(commandBuffer, firstAttachment, attachmentCount, pColorWriteMasks); } +VKAPI_ATTR void VKAPI_CALL CmdSetTessellationDomainOriginEXT( + VkCommandBuffer commandBuffer, + VkTessellationDomainOrigin domainOrigin) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetTessellationDomainOriginEXT: Invalid commandBuffer " + "[VUID-vkCmdSetTessellationDomainOriginEXT-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetTessellationDomainOriginEXT(commandBuffer, domainOrigin); +} + VKAPI_ATTR void VKAPI_CALL CmdSetRasterizationStreamEXT( VkCommandBuffer commandBuffer, uint32_t rasterizationStream) { @@ -9185,6 +9541,22 @@ VKAPI_ATTR void VKAPI_CALL CmdOpticalFlowExecuteNV( } +// ---- VK_AMD_anti_lag extension trampoline/terminators + +VKAPI_ATTR void VKAPI_CALL AntiLagUpdateAMD( + VkDevice device, + const VkAntiLagDataAMD* pData) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkAntiLagUpdateAMD: Invalid device " + "[VUID-vkAntiLagUpdateAMD-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->AntiLagUpdateAMD(device, pData); +} + + // ---- VK_EXT_shader_object extension trampoline/terminators VKAPI_ATTR VkResult VKAPI_CALL CreateShadersEXT( @@ -9247,6 +9619,20 @@ VKAPI_ATTR void VKAPI_CALL CmdBindShadersEXT( disp->CmdBindShadersEXT(commandBuffer, stageCount, pStages, pShaders); } +VKAPI_ATTR void VKAPI_CALL CmdSetDepthClampRangeEXT( + VkCommandBuffer commandBuffer, + VkDepthClampModeEXT depthClampMode, + const VkDepthClampRangeEXT* pDepthClampRange) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetDepthClampRangeEXT: Invalid commandBuffer " + "[VUID-vkCmdSetDepthClampRangeEXT-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetDepthClampRangeEXT(commandBuffer, depthClampMode, pDepthClampRange); +} + // ---- VK_QCOM_tile_properties extension trampoline/terminators @@ -9280,14 +9666,74 @@ VKAPI_ATTR VkResult VKAPI_CALL GetDynamicRenderingTilePropertiesQCOM( } -// ---- VK_NV_low_latency2 extension trampoline/terminators +// ---- VK_NV_cooperative_vector extension trampoline/terminators -VKAPI_ATTR VkResult VKAPI_CALL SetLatencySleepModeNV( - VkDevice device, - VkSwapchainKHR swapchain, - const VkLatencySleepModeInfoNV* pSleepModeInfo) { - const VkLayerDispatchTable *disp = loader_get_dispatch(device); - if (NULL == disp) { +VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceCooperativeVectorPropertiesNV( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkCooperativeVectorPropertiesNV* pProperties) { + const VkLayerInstanceDispatchTable *disp; + VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device(physicalDevice); + if (VK_NULL_HANDLE == unwrapped_phys_dev) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPhysicalDeviceCooperativeVectorPropertiesNV: Invalid physicalDevice " + "[VUID-vkGetPhysicalDeviceCooperativeVectorPropertiesNV-physicalDevice-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp = loader_get_instance_layer_dispatch(physicalDevice); + return disp->GetPhysicalDeviceCooperativeVectorPropertiesNV(unwrapped_phys_dev, pPropertyCount, pProperties); +} + +VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceCooperativeVectorPropertiesNV( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkCooperativeVectorPropertiesNV* pProperties) { + struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)physicalDevice; + struct loader_icd_term *icd_term = phys_dev_term->this_icd_term; + if (NULL == icd_term->dispatch.GetPhysicalDeviceCooperativeVectorPropertiesNV) { + loader_log(icd_term->this_instance, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT, 0, + "ICD associated with VkPhysicalDevice does not support GetPhysicalDeviceCooperativeVectorPropertiesNV"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return icd_term->dispatch.GetPhysicalDeviceCooperativeVectorPropertiesNV(phys_dev_term->phys_dev, pPropertyCount, pProperties); +} + +VKAPI_ATTR VkResult VKAPI_CALL ConvertCooperativeVectorMatrixNV( + VkDevice device, + const VkConvertCooperativeVectorMatrixInfoNV* pInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkConvertCooperativeVectorMatrixNV: Invalid device " + "[VUID-vkConvertCooperativeVectorMatrixNV-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->ConvertCooperativeVectorMatrixNV(device, pInfo); +} + +VKAPI_ATTR void VKAPI_CALL CmdConvertCooperativeVectorMatrixNV( + VkCommandBuffer commandBuffer, + uint32_t infoCount, + const VkConvertCooperativeVectorMatrixInfoNV* pInfos) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdConvertCooperativeVectorMatrixNV: Invalid commandBuffer " + "[VUID-vkCmdConvertCooperativeVectorMatrixNV-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdConvertCooperativeVectorMatrixNV(commandBuffer, infoCount, pInfos); +} + + +// ---- VK_NV_low_latency2 extension trampoline/terminators + +VKAPI_ATTR VkResult VKAPI_CALL SetLatencySleepModeNV( + VkDevice device, + VkSwapchainKHR swapchain, + const VkLatencySleepModeInfoNV* pSleepModeInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "vkSetLatencySleepModeNV: Invalid device " "[VUID-vkSetLatencySleepModeNV-device-parameter]"); @@ -9387,6 +9833,268 @@ VKAPI_ATTR VkResult VKAPI_CALL GetScreenBufferPropertiesQNX( #endif // VK_USE_PLATFORM_SCREEN_QNX +// ---- VK_NV_cluster_acceleration_structure extension trampoline/terminators + +VKAPI_ATTR void VKAPI_CALL GetClusterAccelerationStructureBuildSizesNV( + VkDevice device, + const VkClusterAccelerationStructureInputInfoNV* pInfo, + VkAccelerationStructureBuildSizesInfoKHR* pSizeInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetClusterAccelerationStructureBuildSizesNV: Invalid device " + "[VUID-vkGetClusterAccelerationStructureBuildSizesNV-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetClusterAccelerationStructureBuildSizesNV(device, pInfo, pSizeInfo); +} + +VKAPI_ATTR void VKAPI_CALL CmdBuildClusterAccelerationStructureIndirectNV( + VkCommandBuffer commandBuffer, + const VkClusterAccelerationStructureCommandsInfoNV* pCommandInfos) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBuildClusterAccelerationStructureIndirectNV: Invalid commandBuffer " + "[VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdBuildClusterAccelerationStructureIndirectNV(commandBuffer, pCommandInfos); +} + + +// ---- VK_NV_partitioned_acceleration_structure extension trampoline/terminators + +VKAPI_ATTR void VKAPI_CALL GetPartitionedAccelerationStructuresBuildSizesNV( + VkDevice device, + const VkPartitionedAccelerationStructureInstancesInputNV* pInfo, + VkAccelerationStructureBuildSizesInfoKHR* pSizeInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPartitionedAccelerationStructuresBuildSizesNV: Invalid device " + "[VUID-vkGetPartitionedAccelerationStructuresBuildSizesNV-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetPartitionedAccelerationStructuresBuildSizesNV(device, pInfo, pSizeInfo); +} + +VKAPI_ATTR void VKAPI_CALL CmdBuildPartitionedAccelerationStructuresNV( + VkCommandBuffer commandBuffer, + const VkBuildPartitionedAccelerationStructureInfoNV* pBuildInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBuildPartitionedAccelerationStructuresNV: Invalid commandBuffer " + "[VUID-vkCmdBuildPartitionedAccelerationStructuresNV-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdBuildPartitionedAccelerationStructuresNV(commandBuffer, pBuildInfo); +} + + +// ---- VK_EXT_device_generated_commands extension trampoline/terminators + +VKAPI_ATTR void VKAPI_CALL GetGeneratedCommandsMemoryRequirementsEXT( + VkDevice device, + const VkGeneratedCommandsMemoryRequirementsInfoEXT* pInfo, + VkMemoryRequirements2* pMemoryRequirements) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetGeneratedCommandsMemoryRequirementsEXT: Invalid device " + "[VUID-vkGetGeneratedCommandsMemoryRequirementsEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetGeneratedCommandsMemoryRequirementsEXT(device, pInfo, pMemoryRequirements); +} + +VKAPI_ATTR void VKAPI_CALL CmdPreprocessGeneratedCommandsEXT( + VkCommandBuffer commandBuffer, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + VkCommandBuffer stateCommandBuffer) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPreprocessGeneratedCommandsEXT: Invalid commandBuffer " + "[VUID-vkCmdPreprocessGeneratedCommandsEXT-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdPreprocessGeneratedCommandsEXT(commandBuffer, pGeneratedCommandsInfo, stateCommandBuffer); +} + +VKAPI_ATTR void VKAPI_CALL CmdExecuteGeneratedCommandsEXT( + VkCommandBuffer commandBuffer, + VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdExecuteGeneratedCommandsEXT: Invalid commandBuffer " + "[VUID-vkCmdExecuteGeneratedCommandsEXT-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdExecuteGeneratedCommandsEXT(commandBuffer, isPreprocessed, pGeneratedCommandsInfo); +} + +VKAPI_ATTR VkResult VKAPI_CALL CreateIndirectCommandsLayoutEXT( + VkDevice device, + const VkIndirectCommandsLayoutCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutEXT* pIndirectCommandsLayout) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCreateIndirectCommandsLayoutEXT: Invalid device " + "[VUID-vkCreateIndirectCommandsLayoutEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->CreateIndirectCommandsLayoutEXT(device, pCreateInfo, pAllocator, pIndirectCommandsLayout); +} + +VKAPI_ATTR void VKAPI_CALL DestroyIndirectCommandsLayoutEXT( + VkDevice device, + VkIndirectCommandsLayoutEXT indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkDestroyIndirectCommandsLayoutEXT: Invalid device " + "[VUID-vkDestroyIndirectCommandsLayoutEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->DestroyIndirectCommandsLayoutEXT(device, indirectCommandsLayout, pAllocator); +} + +VKAPI_ATTR VkResult VKAPI_CALL CreateIndirectExecutionSetEXT( + VkDevice device, + const VkIndirectExecutionSetCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectExecutionSetEXT* pIndirectExecutionSet) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCreateIndirectExecutionSetEXT: Invalid device " + "[VUID-vkCreateIndirectExecutionSetEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->CreateIndirectExecutionSetEXT(device, pCreateInfo, pAllocator, pIndirectExecutionSet); +} + +VKAPI_ATTR void VKAPI_CALL DestroyIndirectExecutionSetEXT( + VkDevice device, + VkIndirectExecutionSetEXT indirectExecutionSet, + const VkAllocationCallbacks* pAllocator) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkDestroyIndirectExecutionSetEXT: Invalid device " + "[VUID-vkDestroyIndirectExecutionSetEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->DestroyIndirectExecutionSetEXT(device, indirectExecutionSet, pAllocator); +} + +VKAPI_ATTR void VKAPI_CALL UpdateIndirectExecutionSetPipelineEXT( + VkDevice device, + VkIndirectExecutionSetEXT indirectExecutionSet, + uint32_t executionSetWriteCount, + const VkWriteIndirectExecutionSetPipelineEXT* pExecutionSetWrites) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkUpdateIndirectExecutionSetPipelineEXT: Invalid device " + "[VUID-vkUpdateIndirectExecutionSetPipelineEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->UpdateIndirectExecutionSetPipelineEXT(device, indirectExecutionSet, executionSetWriteCount, pExecutionSetWrites); +} + +VKAPI_ATTR void VKAPI_CALL UpdateIndirectExecutionSetShaderEXT( + VkDevice device, + VkIndirectExecutionSetEXT indirectExecutionSet, + uint32_t executionSetWriteCount, + const VkWriteIndirectExecutionSetShaderEXT* pExecutionSetWrites) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkUpdateIndirectExecutionSetShaderEXT: Invalid device " + "[VUID-vkUpdateIndirectExecutionSetShaderEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->UpdateIndirectExecutionSetShaderEXT(device, indirectExecutionSet, executionSetWriteCount, pExecutionSetWrites); +} + + +// ---- VK_NV_cooperative_matrix2 extension trampoline/terminators + +VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkCooperativeMatrixFlexibleDimensionsPropertiesNV* pProperties) { + const VkLayerInstanceDispatchTable *disp; + VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device(physicalDevice); + if (VK_NULL_HANDLE == unwrapped_phys_dev) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV: Invalid physicalDevice " + "[VUID-vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV-physicalDevice-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp = loader_get_instance_layer_dispatch(physicalDevice); + return disp->GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV(unwrapped_phys_dev, pPropertyCount, pProperties); +} + +VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkCooperativeMatrixFlexibleDimensionsPropertiesNV* pProperties) { + struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)physicalDevice; + struct loader_icd_term *icd_term = phys_dev_term->this_icd_term; + if (NULL == icd_term->dispatch.GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV) { + loader_log(icd_term->this_instance, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT, 0, + "ICD associated with VkPhysicalDevice does not support GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return icd_term->dispatch.GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV(phys_dev_term->phys_dev, pPropertyCount, pProperties); +} + + +// ---- VK_EXT_external_memory_metal extension trampoline/terminators + +#if defined(VK_USE_PLATFORM_METAL_EXT) +VKAPI_ATTR VkResult VKAPI_CALL GetMemoryMetalHandleEXT( + VkDevice device, + const VkMemoryGetMetalHandleInfoEXT* pGetMetalHandleInfo, + void** pHandle) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetMemoryMetalHandleEXT: Invalid device " + "[VUID-vkGetMemoryMetalHandleEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->GetMemoryMetalHandleEXT(device, pGetMetalHandleInfo, pHandle); +} + +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_METAL_EXT) +VKAPI_ATTR VkResult VKAPI_CALL GetMemoryMetalHandlePropertiesEXT( + VkDevice device, + VkExternalMemoryHandleTypeFlagBits handleType, + const void* pHandle, + VkMemoryMetalHandlePropertiesEXT* pMemoryMetalHandleProperties) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetMemoryMetalHandlePropertiesEXT: Invalid device " + "[VUID-vkGetMemoryMetalHandlePropertiesEXT-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->GetMemoryMetalHandlePropertiesEXT(device, handleType, pHandle, pMemoryMetalHandleProperties); +} + +#endif // VK_USE_PLATFORM_METAL_EXT + // ---- VK_KHR_acceleration_structure extension trampoline/terminators VKAPI_ATTR VkResult VKAPI_CALL CreateAccelerationStructureKHR( @@ -10249,6 +10957,16 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na return true; } + // ---- VK_KHR_dynamic_rendering_local_read extension commands + if (!strcmp("vkCmdSetRenderingAttachmentLocationsKHR", name)) { + *addr = (void *)CmdSetRenderingAttachmentLocationsKHR; + return true; + } + if (!strcmp("vkCmdSetRenderingInputAttachmentIndicesKHR", name)) { + *addr = (void *)CmdSetRenderingInputAttachmentIndicesKHR; + return true; + } + // ---- VK_KHR_present_wait extension commands if (!strcmp("vkWaitForPresentKHR", name)) { *addr = (void *)WaitForPresentKHR; @@ -10354,14 +11072,6 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na *addr = (void *)QueueSubmit2KHR; return true; } - if (!strcmp("vkCmdWriteBufferMarker2AMD", name)) { - *addr = (void *)CmdWriteBufferMarker2AMD; - return true; - } - if (!strcmp("vkGetQueueCheckpointData2NV", name)) { - *addr = (void *)GetQueueCheckpointData2NV; - return true; - } // ---- VK_KHR_copy_commands2 extension commands if (!strcmp("vkCmdCopyBuffer2KHR", name)) { @@ -10427,12 +11137,40 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na return true; } + // ---- VK_KHR_pipeline_binary extension commands + if (!strcmp("vkCreatePipelineBinariesKHR", name)) { + *addr = (void *)CreatePipelineBinariesKHR; + return true; + } + if (!strcmp("vkDestroyPipelineBinaryKHR", name)) { + *addr = (void *)DestroyPipelineBinaryKHR; + return true; + } + if (!strcmp("vkGetPipelineKeyKHR", name)) { + *addr = (void *)GetPipelineKeyKHR; + return true; + } + if (!strcmp("vkGetPipelineBinaryDataKHR", name)) { + *addr = (void *)GetPipelineBinaryDataKHR; + return true; + } + if (!strcmp("vkReleaseCapturedPipelineDataKHR", name)) { + *addr = (void *)ReleaseCapturedPipelineDataKHR; + return true; + } + // ---- VK_KHR_cooperative_matrix extension commands if (!strcmp("vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR", name)) { *addr = (void *)GetPhysicalDeviceCooperativeMatrixPropertiesKHR; return true; } + // ---- VK_KHR_line_rasterization extension commands + if (!strcmp("vkCmdSetLineStippleKHR", name)) { + *addr = (void *)CmdSetLineStippleKHR; + return true; + } + // ---- VK_KHR_calibrated_timestamps extension commands if (!strcmp("vkGetPhysicalDeviceCalibrateableTimeDomainsKHR", name)) { *addr = (void *)GetPhysicalDeviceCalibrateableTimeDomainsKHR; @@ -10544,6 +11282,10 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na *addr = (void *)GetImageViewHandleNVX; return true; } + if (!strcmp("vkGetImageViewHandle64NVX", name)) { + *addr = (void *)GetImageViewHandle64NVX; + return true; + } if (!strcmp("vkGetImageViewAddressNVX", name)) { *addr = (void *)GetImageViewAddressNVX; return true; @@ -10900,6 +11642,10 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na *addr = (void *)CmdWriteBufferMarkerAMD; return true; } + if (!strcmp("vkCmdWriteBufferMarker2AMD", name)) { + *addr = (void *)CmdWriteBufferMarker2AMD; + return true; + } // ---- VK_EXT_calibrated_timestamps extension commands if (!strcmp("vkGetPhysicalDeviceCalibrateableTimeDomainsEXT", name)) { @@ -10944,6 +11690,10 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na *addr = (void *)GetQueueCheckpointDataNV; return true; } + if (!strcmp("vkGetQueueCheckpointData2NV", name)) { + *addr = (void *)GetQueueCheckpointData2NV; + return true; + } // ---- VK_INTEL_performance_query extension commands if (!strcmp("vkInitializePerformanceApiINTEL", name)) { @@ -11550,10 +12300,6 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na } // ---- VK_EXT_extended_dynamic_state3 extension commands - if (!strcmp("vkCmdSetTessellationDomainOriginEXT", name)) { - *addr = (void *)CmdSetTessellationDomainOriginEXT; - return true; - } if (!strcmp("vkCmdSetDepthClampEnableEXT", name)) { *addr = (void *)CmdSetDepthClampEnableEXT; return true; @@ -11594,6 +12340,10 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na *addr = (void *)CmdSetColorWriteMaskEXT; return true; } + if (!strcmp("vkCmdSetTessellationDomainOriginEXT", name)) { + *addr = (void *)CmdSetTessellationDomainOriginEXT; + return true; + } if (!strcmp("vkCmdSetRasterizationStreamEXT", name)) { *addr = (void *)CmdSetRasterizationStreamEXT; return true; @@ -11707,6 +12457,12 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na return true; } + // ---- VK_AMD_anti_lag extension commands + if (!strcmp("vkAntiLagUpdateAMD", name)) { + *addr = (void *)AntiLagUpdateAMD; + return true; + } + // ---- VK_EXT_shader_object extension commands if (!strcmp("vkCreateShadersEXT", name)) { *addr = (void *)CreateShadersEXT; @@ -11724,6 +12480,10 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na *addr = (void *)CmdBindShadersEXT; return true; } + if (!strcmp("vkCmdSetDepthClampRangeEXT", name)) { + *addr = (void *)CmdSetDepthClampRangeEXT; + return true; + } // ---- VK_QCOM_tile_properties extension commands if (!strcmp("vkGetFramebufferTilePropertiesQCOM", name)) { @@ -11735,6 +12495,20 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na return true; } + // ---- VK_NV_cooperative_vector extension commands + if (!strcmp("vkGetPhysicalDeviceCooperativeVectorPropertiesNV", name)) { + *addr = (void *)GetPhysicalDeviceCooperativeVectorPropertiesNV; + return true; + } + if (!strcmp("vkConvertCooperativeVectorMatrixNV", name)) { + *addr = (void *)ConvertCooperativeVectorMatrixNV; + return true; + } + if (!strcmp("vkCmdConvertCooperativeVectorMatrixNV", name)) { + *addr = (void *)CmdConvertCooperativeVectorMatrixNV; + return true; + } + // ---- VK_NV_low_latency2 extension commands if (!strcmp("vkSetLatencySleepModeNV", name)) { *addr = (void *)SetLatencySleepModeNV; @@ -11771,6 +12545,84 @@ bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *na } #endif // VK_USE_PLATFORM_SCREEN_QNX + // ---- VK_NV_cluster_acceleration_structure extension commands + if (!strcmp("vkGetClusterAccelerationStructureBuildSizesNV", name)) { + *addr = (void *)GetClusterAccelerationStructureBuildSizesNV; + return true; + } + if (!strcmp("vkCmdBuildClusterAccelerationStructureIndirectNV", name)) { + *addr = (void *)CmdBuildClusterAccelerationStructureIndirectNV; + return true; + } + + // ---- VK_NV_partitioned_acceleration_structure extension commands + if (!strcmp("vkGetPartitionedAccelerationStructuresBuildSizesNV", name)) { + *addr = (void *)GetPartitionedAccelerationStructuresBuildSizesNV; + return true; + } + if (!strcmp("vkCmdBuildPartitionedAccelerationStructuresNV", name)) { + *addr = (void *)CmdBuildPartitionedAccelerationStructuresNV; + return true; + } + + // ---- VK_EXT_device_generated_commands extension commands + if (!strcmp("vkGetGeneratedCommandsMemoryRequirementsEXT", name)) { + *addr = (void *)GetGeneratedCommandsMemoryRequirementsEXT; + return true; + } + if (!strcmp("vkCmdPreprocessGeneratedCommandsEXT", name)) { + *addr = (void *)CmdPreprocessGeneratedCommandsEXT; + return true; + } + if (!strcmp("vkCmdExecuteGeneratedCommandsEXT", name)) { + *addr = (void *)CmdExecuteGeneratedCommandsEXT; + return true; + } + if (!strcmp("vkCreateIndirectCommandsLayoutEXT", name)) { + *addr = (void *)CreateIndirectCommandsLayoutEXT; + return true; + } + if (!strcmp("vkDestroyIndirectCommandsLayoutEXT", name)) { + *addr = (void *)DestroyIndirectCommandsLayoutEXT; + return true; + } + if (!strcmp("vkCreateIndirectExecutionSetEXT", name)) { + *addr = (void *)CreateIndirectExecutionSetEXT; + return true; + } + if (!strcmp("vkDestroyIndirectExecutionSetEXT", name)) { + *addr = (void *)DestroyIndirectExecutionSetEXT; + return true; + } + if (!strcmp("vkUpdateIndirectExecutionSetPipelineEXT", name)) { + *addr = (void *)UpdateIndirectExecutionSetPipelineEXT; + return true; + } + if (!strcmp("vkUpdateIndirectExecutionSetShaderEXT", name)) { + *addr = (void *)UpdateIndirectExecutionSetShaderEXT; + return true; + } + + // ---- VK_NV_cooperative_matrix2 extension commands + if (!strcmp("vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV", name)) { + *addr = (void *)GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV; + return true; + } + + // ---- VK_EXT_external_memory_metal extension commands +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (!strcmp("vkGetMemoryMetalHandleEXT", name)) { + *addr = (void *)GetMemoryMetalHandleEXT; + return true; + } +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (!strcmp("vkGetMemoryMetalHandlePropertiesEXT", name)) { + *addr = (void *)GetMemoryMetalHandlePropertiesEXT; + return true; + } +#endif // VK_USE_PLATFORM_METAL_EXT + // ---- VK_KHR_acceleration_structure extension commands if (!strcmp("vkCreateAccelerationStructureKHR", name)) { *addr = (void *)CreateAccelerationStructureKHR; @@ -12049,7 +12901,7 @@ PFN_vkVoidFunction get_extension_device_proc_terminator(struct loader_device *de // ---- VK_EXT_full_screen_exclusive extension commands if (!strcmp(name, "GetDeviceGroupSurfacePresentModes2EXT")) { *found_name = true; - return dev->driver_extensions.ext_full_screen_exclusive_enabled && dev->driver_extensions.khr_device_group_enabled ? + return (dev->driver_extensions.ext_full_screen_exclusive_enabled && (dev->driver_extensions.khr_device_group_enabled || dev->driver_extensions.version_1_1_enabled)) ? (PFN_vkVoidFunction)terminator_GetDeviceGroupSurfacePresentModes2EXT : NULL; } #endif // VK_USE_PLATFORM_WIN32_KHR @@ -12312,6 +13164,12 @@ const VkLayerInstanceDispatchTable instance_disp = { #if defined(VK_USE_PLATFORM_OHOS) .CreateSurfaceOHOS = terminator_CreateSurfaceOHOS, #endif // VK_USE_PLATFORM_OHOS + + // ---- VK_NV_cooperative_vector extension commands + .GetPhysicalDeviceCooperativeVectorPropertiesNV = terminator_GetPhysicalDeviceCooperativeVectorPropertiesNV, + + // ---- VK_NV_cooperative_matrix2 extension commands + .GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV = terminator_GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV, }; // A null-terminated list of all of the instance extensions supported by the loader. @@ -12383,6 +13241,7 @@ const char *const LOADER_INSTANCE_EXTENSIONS[] = { VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME, VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, + VK_NV_DISPLAY_STEREO_EXTENSION_NAME, #if defined(VK_USE_PLATFORM_OHOS) VK_OHOS_SURFACE_EXTENSION_NAME, #endif // VK_USE_PLATFORM_OHOS diff --git a/loader/generated/vk_loader_extensions.h b/loader/generated/vk_loader_extensions.h index 2a1d1ad44601ec5c054438ed022ba651dfe96392..8958d22bc75ec77e8e06f14ced4b3cb8d242586f 100644 --- a/loader/generated/vk_loader_extensions.h +++ b/loader/generated/vk_loader_extensions.h @@ -27,6 +27,12 @@ // clang-format off #pragma once +#include +#include +#include +#include "vk_layer_dispatch_table.h" + + // Structures defined externally, but used here struct loader_instance; struct loader_device; @@ -131,6 +137,10 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice( const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceExtensionProperties( + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); +VKAPI_ATTR VkResult VKAPI_CALL terminator_pre_instance_EnumerateInstanceExtensionProperties( const VkEnumerateInstanceExtensionPropertiesChain* chain, const char* pLayerName, uint32_t* pPropertyCount, @@ -141,6 +151,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateDeviceExtensionProperties( uint32_t* pPropertyCount, VkExtensionProperties* pProperties); VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceLayerProperties( + uint32_t* pPropertyCount, + VkLayerProperties* pProperties); +VKAPI_ATTR VkResult VKAPI_CALL terminator_pre_instance_EnumerateInstanceLayerProperties( const VkEnumerateInstanceLayerPropertiesChain* chain, uint32_t* pPropertyCount, VkLayerProperties* pProperties); @@ -158,6 +171,8 @@ VKAPI_ATTR void VKAPI_CALL terminator_GetPhysicalDeviceSparseImageFormatProperti uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties); VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceVersion( + uint32_t* pApiVersion); +VKAPI_ATTR VkResult VKAPI_CALL terminator_pre_instance_EnumerateInstanceVersion( const VkEnumerateInstanceVersionChain* chain, uint32_t* pApiVersion); VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( @@ -466,6 +481,12 @@ struct loader_icd_term_dispatch { #ifdef VK_USE_PLATFORM_OHOS PFN_vkCreateSurfaceOHOS CreateSurfaceOHOS; #endif // VK_USE_PLATFORM_OHOS + + // ---- VK_NV_cooperative_vector extension commands + PFN_vkGetPhysicalDeviceCooperativeVectorPropertiesNV GetPhysicalDeviceCooperativeVectorPropertiesNV; + + // ---- VK_NV_cooperative_matrix2 extension commands + PFN_vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV GetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV; }; struct loader_instance_extension_enables { diff --git a/loader/generated/vk_object_types.h b/loader/generated/vk_object_types.h index 08f3bef7891d0aa3446c9f198f73674d61c8cba7..c7666efbdf15f3fc3dd4a5e148d15da0cde1d0f1 100644 --- a/loader/generated/vk_object_types.h +++ b/loader/generated/vk_object_types.h @@ -73,22 +73,25 @@ typedef enum VulkanObjectType { kVulkanObjectTypeVideoSessionKHR = 33, kVulkanObjectTypeVideoSessionParametersKHR = 34, kVulkanObjectTypeDeferredOperationKHR = 35, - kVulkanObjectTypeDebugReportCallbackEXT = 36, - kVulkanObjectTypeCuModuleNVX = 37, - kVulkanObjectTypeCuFunctionNVX = 38, - kVulkanObjectTypeDebugUtilsMessengerEXT = 39, - kVulkanObjectTypeValidationCacheEXT = 40, - kVulkanObjectTypeAccelerationStructureNV = 41, - kVulkanObjectTypePerformanceConfigurationINTEL = 42, - kVulkanObjectTypeIndirectCommandsLayoutNV = 43, - kVulkanObjectTypeCudaModuleNV = 44, - kVulkanObjectTypeCudaFunctionNV = 45, - kVulkanObjectTypeAccelerationStructureKHR = 46, - kVulkanObjectTypeBufferCollectionFUCHSIA = 47, - kVulkanObjectTypeMicromapEXT = 48, - kVulkanObjectTypeOpticalFlowSessionNV = 49, - kVulkanObjectTypeShaderEXT = 50, - kVulkanObjectTypeMax = 51, + kVulkanObjectTypePipelineBinaryKHR = 36, + kVulkanObjectTypeDebugReportCallbackEXT = 37, + kVulkanObjectTypeCuModuleNVX = 38, + kVulkanObjectTypeCuFunctionNVX = 39, + kVulkanObjectTypeDebugUtilsMessengerEXT = 40, + kVulkanObjectTypeValidationCacheEXT = 41, + kVulkanObjectTypeAccelerationStructureNV = 42, + kVulkanObjectTypePerformanceConfigurationINTEL = 43, + kVulkanObjectTypeIndirectCommandsLayoutNV = 44, + kVulkanObjectTypeCudaModuleNV = 45, + kVulkanObjectTypeCudaFunctionNV = 46, + kVulkanObjectTypeAccelerationStructureKHR = 47, + kVulkanObjectTypeBufferCollectionFUCHSIA = 48, + kVulkanObjectTypeMicromapEXT = 49, + kVulkanObjectTypeOpticalFlowSessionNV = 50, + kVulkanObjectTypeShaderEXT = 51, + kVulkanObjectTypeIndirectExecutionSetEXT = 52, + kVulkanObjectTypeIndirectCommandsLayoutEXT = 53, + kVulkanObjectTypeMax = 54, // Aliases for backwards compatibilty of "promoted" types kVulkanObjectTypeDescriptorUpdateTemplateKHR = kVulkanObjectTypeDescriptorUpdateTemplate, kVulkanObjectTypeSamplerYcbcrConversionKHR = kVulkanObjectTypeSamplerYcbcrConversion, @@ -133,6 +136,7 @@ static const char * const object_string[kVulkanObjectTypeMax] = { "VideoSessionKHR", "VideoSessionParametersKHR", "DeferredOperationKHR", + "PipelineBinaryKHR", "DebugReportCallbackEXT", "CuModuleNVX", "CuFunctionNVX", @@ -148,6 +152,8 @@ static const char * const object_string[kVulkanObjectTypeMax] = { "MicromapEXT", "OpticalFlowSessionNV", "ShaderEXT", + "IndirectExecutionSetEXT", + "IndirectCommandsLayoutEXT", }; // Helper array to get Vulkan VK_EXT_debug_report object type enum from the internal layers version @@ -188,6 +194,7 @@ const VkDebugReportObjectTypeEXT get_debug_report_enum[] = { VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeVideoSessionKHR VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeVideoSessionParametersKHR VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeDeferredOperationKHR + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypePipelineBinaryKHR VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, // kVulkanObjectTypeDebugReportCallbackEXT VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT, // kVulkanObjectTypeCuModuleNVX VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT, // kVulkanObjectTypeCuFunctionNVX @@ -203,6 +210,8 @@ const VkDebugReportObjectTypeEXT get_debug_report_enum[] = { VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeMicromapEXT VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeOpticalFlowSessionNV VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeShaderEXT + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeIndirectExecutionSetEXT + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeIndirectCommandsLayoutEXT }; // Helper array to get Official Vulkan VkObjectType enum from the internal layers version @@ -243,6 +252,7 @@ const VkObjectType get_object_type_enum[] = { VK_OBJECT_TYPE_VIDEO_SESSION_KHR, // kVulkanObjectTypeVideoSessionKHR VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR, // kVulkanObjectTypeVideoSessionParametersKHR VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR, // kVulkanObjectTypeDeferredOperationKHR + VK_OBJECT_TYPE_PIPELINE_BINARY_KHR, // kVulkanObjectTypePipelineBinaryKHR VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT, // kVulkanObjectTypeDebugReportCallbackEXT VK_OBJECT_TYPE_CU_MODULE_NVX, // kVulkanObjectTypeCuModuleNVX VK_OBJECT_TYPE_CU_FUNCTION_NVX, // kVulkanObjectTypeCuFunctionNVX @@ -258,6 +268,8 @@ const VkObjectType get_object_type_enum[] = { VK_OBJECT_TYPE_MICROMAP_EXT, // kVulkanObjectTypeMicromapEXT VK_OBJECT_TYPE_OPTICAL_FLOW_SESSION_NV, // kVulkanObjectTypeOpticalFlowSessionNV VK_OBJECT_TYPE_SHADER_EXT, // kVulkanObjectTypeShaderEXT + VK_OBJECT_TYPE_INDIRECT_EXECUTION_SET_EXT, // kVulkanObjectTypeIndirectExecutionSetEXT + VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_EXT, // kVulkanObjectTypeIndirectCommandsLayoutEXT }; // Helper function to convert from VkDebugReportObjectTypeEXT to VkObjectType diff --git a/loader/gpa_helper.c b/loader/gpa_helper.c index b0aeb5c3c75204523542b69a3eff73fecb50c184..09e7ede63501d3f4ebff362f4f409f363711b44d 100644 --- a/loader/gpa_helper.c +++ b/loader/gpa_helper.c @@ -1,8 +1,8 @@ /* * - * Copyright (c) 2015-2021 The Khronos Group Inc. - * Copyright (c) 2015-2021 Valve Corporation - * Copyright (c) 2015-2021 LunarG, Inc. + * Copyright (c) 2015-2024 The Khronos Group Inc. + * Copyright (c) 2015-2024 Valve Corporation + * Copyright (c) 2015-2024 LunarG, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ #include -#include "loader.h" #include "debug_utils.h" #include "unknown_function_handling.h" #include "wsi.h" @@ -250,6 +249,27 @@ void *trampoline_get_proc_addr(struct loader_instance *inst, const char *funcNam if (!strcmp(funcName, "vkGetDeviceImageMemoryRequirements")) return vkGetDeviceImageMemoryRequirements; if (!strcmp(funcName, "vkGetDeviceImageSparseMemoryRequirements")) return vkGetDeviceImageSparseMemoryRequirements; + // Core 1.4 functions + if (!strcmp(funcName, "vkCmdSetLineStipple")) return vkCmdSetLineStipple; + if (!strcmp(funcName, "vkMapMemory2")) return vkMapMemory2; + if (!strcmp(funcName, "vkUnmapMemory2")) return vkUnmapMemory2; + if (!strcmp(funcName, "vkCmdBindIndexBuffer2")) return vkCmdBindIndexBuffer2; + if (!strcmp(funcName, "vkGetRenderingAreaGranularity")) return vkGetRenderingAreaGranularity; + if (!strcmp(funcName, "vkGetDeviceImageSubresourceLayout")) return vkGetDeviceImageSubresourceLayout; + if (!strcmp(funcName, "vkGetImageSubresourceLayout2")) return vkGetImageSubresourceLayout2; + if (!strcmp(funcName, "vkCmdPushDescriptorSet")) return vkCmdPushDescriptorSet; + if (!strcmp(funcName, "vkCmdPushDescriptorSetWithTemplate")) return vkCmdPushDescriptorSetWithTemplate; + if (!strcmp(funcName, "vkCmdSetRenderingAttachmentLocations")) return vkCmdSetRenderingAttachmentLocations; + if (!strcmp(funcName, "vkCmdSetRenderingInputAttachmentIndices")) return vkCmdSetRenderingInputAttachmentIndices; + if (!strcmp(funcName, "vkCmdBindDescriptorSets2")) return vkCmdBindDescriptorSets2; + if (!strcmp(funcName, "vkCmdPushConstants2")) return vkCmdPushConstants2; + if (!strcmp(funcName, "vkCmdPushDescriptorSet2")) return vkCmdPushDescriptorSet2; + if (!strcmp(funcName, "vkCmdPushDescriptorSetWithTemplate2")) return vkCmdPushDescriptorSetWithTemplate2; + if (!strcmp(funcName, "vkCopyMemoryToImage")) return vkCopyMemoryToImage; + if (!strcmp(funcName, "vkCopyImageToMemory")) return vkCopyImageToMemory; + if (!strcmp(funcName, "vkCopyImageToImage")) return vkCopyImageToImage; + if (!strcmp(funcName, "vkTransitionImageLayout")) return vkTransitionImageLayout; + // Instance extensions void *addr; if (debug_extensions_InstanceGpa(inst, funcName, &addr)) return addr; diff --git a/loader/loader.c b/loader/loader.c index 5719d5411414d97eba99ecb65ea48e96afb78a7a..2cfdfb5d4a3277418ed5db8b824debe1ba044f87 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -30,7 +30,7 @@ #include "loader.h" -#include +#include #include #include #include @@ -54,10 +54,11 @@ #endif // _WIN32 #include "allocation.h" +#include "stack_allocation.h" #include "cJSON.h" #include "debug_utils.h" #include "loader_environment.h" -#include "gpa_helper.h" +#include "loader_json.h" #include "log.h" #include "unknown_function_handling.h" #include "vk_loader_platform.h" @@ -85,7 +86,10 @@ struct activated_layer_info { char *manifest; char *library; bool is_implicit; + enum loader_layer_enabled_by_what enabled_by_what; char *disable_env; + char *enable_name_env; + char *enable_value_env; }; // thread safety lock for accessing global data structures such as "loader" @@ -100,7 +104,7 @@ loader_platform_thread_mutex loader_global_instance_list_lock; // functionality, but the fact that the libraries already been loaded causes any call that needs to load ICD libraries to speed up // significantly. This can have a huge impact when making repeated calls to vkEnumerateInstanceExtensionProperties and // vkCreateInstance. -struct loader_icd_tramp_list scanned_icds; +struct loader_icd_tramp_list preloaded_icds; // controls whether loader_platform_close_library() closes the libraries or not - controlled by an environment // variables - this is just the definition of the variable, usage is in vk_loader_platform.h @@ -144,6 +148,29 @@ bool loader_check_version_meets_required(loader_api_version required, loader_api (version.major == required.major && version.minor == required.minor && version.patch >= required.patch); } +const char *get_enabled_by_what_str(enum loader_layer_enabled_by_what enabled_by_what) { + switch (enabled_by_what) { + default: + assert(true && "Shouldn't reach this"); + return "Unknown"; + case (ENABLED_BY_WHAT_UNSET): + assert(true && "Shouldn't reach this"); + return "Unknown"; + case (ENABLED_BY_WHAT_LOADER_SETTINGS_FILE): + return "Loader Settings File (Vulkan Configurator)"; + case (ENABLED_BY_WHAT_IMPLICIT_LAYER): + return "Implicit Layer"; + case (ENABLED_BY_WHAT_VK_INSTANCE_LAYERS): + return "Environment Variable VK_INSTANCE_LAYERS"; + case (ENABLED_BY_WHAT_VK_LOADER_LAYERS_ENABLE): + return "Environment Variable VK_LOADER_LAYERS_ENABLE"; + case (ENABLED_BY_WHAT_IN_APPLICATION_API): + return "By the Application"; + case (ENABLED_BY_WHAT_META_LAYER): + return "Meta Layer (Vulkan Configurator)"; + } +} + // Wrapper around opendir so that the dirent_on_windows gets the instance it needs // while linux opendir & readdir does not DIR *loader_opendir(const struct loader_instance *instance, const char *name) { @@ -198,7 +225,7 @@ void loader_handle_load_library_error(const struct loader_instance *inst, const } else if (NULL != lib_status) { *lib_status = LOADER_LAYER_LIB_ERROR_FAILED_TO_LOAD; } - loader_log(inst, err_flag, 0, error_message); + loader_log(inst, err_flag, 0, "%s", error_message); } VKAPI_ATTR VkResult VKAPI_CALL vkSetInstanceDispatch(VkInstance instance, void *object) { @@ -213,7 +240,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkSetInstanceDispatch(VkInstance instance, void * VKAPI_ATTR VkResult VKAPI_CALL vkSetDeviceDispatch(VkDevice device, void *object) { struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, NULL); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { return VK_ERROR_INITIALIZATION_FAILED; @@ -289,6 +316,7 @@ VkResult append_str_to_string_list(const struct loader_instance *inst, struct lo string_list->list = loader_instance_heap_calloc(inst, sizeof(char *) * string_list->allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == string_list->list) { + loader_instance_heap_free(inst, str); // Must clean up in case of failure return VK_ERROR_OUT_OF_HOST_MEMORY; } } else if (string_list->count + 1 > string_list->allocated_count) { @@ -296,10 +324,9 @@ VkResult append_str_to_string_list(const struct loader_instance *inst, struct lo string_list->list = loader_instance_heap_realloc(inst, string_list->list, sizeof(char *) * string_list->allocated_count, sizeof(char *) * new_allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == string_list->list) { + loader_instance_heap_free(inst, str); // Must clean up in case of failure return VK_ERROR_OUT_OF_HOST_MEMORY; } - // Null out the new space - memset(string_list->list + string_list->allocated_count, 0, string_list->allocated_count); string_list->allocated_count *= 2; } string_list->list[string_list->count++] = str; @@ -315,12 +342,7 @@ VkResult copy_str_to_string_list(const struct loader_instance *inst, struct load } loader_strncpy(new_str, sizeof(char *) * str_len + 1, str, str_len); new_str[str_len] = '\0'; - VkResult res = append_str_to_string_list(inst, string_list, new_str); - if (res != VK_SUCCESS) { - // Cleanup new_str if the append failed - as append_str_to_string_list takes ownership but not if the function fails - loader_instance_heap_free(inst, new_str); - } - return res; + return append_str_to_string_list(inst, string_list, new_str); } void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list) { @@ -419,7 +441,6 @@ VkResult loader_append_layer_property(const struct loader_instance *inst, struct goto out; } layer_list->list = new_ptr; - memset((uint8_t *)layer_list->list + layer_list->capacity, 0, layer_list->capacity); layer_list->capacity *= 2; } memcpy(&layer_list->list[layer_list->count], layer_property, sizeof(struct loader_layer_properties)); @@ -488,7 +509,8 @@ bool loader_find_layer_name_in_blacklist(const char *layer_name, struct loader_l } // Remove all layer properties entries from the list -void loader_delete_layer_list_and_properties(const struct loader_instance *inst, struct loader_layer_list *layer_list) { +TEST_FUNCTION_EXPORT void loader_delete_layer_list_and_properties(const struct loader_instance *inst, + struct loader_layer_list *layer_list) { uint32_t i; if (!layer_list) return; @@ -519,9 +541,10 @@ void loader_remove_layer_in_list(const struct loader_instance *inst, struct load // Remove the current invalid meta-layer from the layer list. Use memmove since we are // overlapping the source and destination addresses. - memmove(&layer_list->list[layer_to_remove], &layer_list->list[layer_to_remove + 1], - sizeof(struct loader_layer_properties) * (layer_list->count - 1 - layer_to_remove)); - + if (layer_to_remove + 1 <= layer_list->count) { + memmove(&layer_list->list[layer_to_remove], &layer_list->list[layer_to_remove + 1], + sizeof(struct loader_layer_properties) * (layer_list->count - 1 - layer_to_remove)); + } // Decrement the count (because we now have one less) and decrement the loop index since we need to // re-check this index. layer_list->count--; @@ -718,11 +741,70 @@ VkResult loader_init_generic_list(const struct loader_instance *inst, struct loa return VK_SUCCESS; } +VkResult loader_resize_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info) { + list_info->list = loader_instance_heap_realloc(inst, list_info->list, list_info->capacity, list_info->capacity * 2, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (list_info->list == NULL) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_resize_generic_list: Failed to allocate space for generic list"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + list_info->capacity = list_info->capacity * 2; + return VK_SUCCESS; +} + void loader_destroy_generic_list(const struct loader_instance *inst, struct loader_generic_list *list) { loader_instance_heap_free(inst, list->list); memset(list, 0, sizeof(struct loader_generic_list)); } +VkResult loader_get_next_available_entry(const struct loader_instance *inst, struct loader_used_object_list *list_info, + uint32_t *free_index, const VkAllocationCallbacks *pAllocator) { + if (NULL == list_info->list) { + VkResult res = + loader_init_generic_list(inst, (struct loader_generic_list *)list_info, sizeof(struct loader_used_object_status)); + if (VK_SUCCESS != res) { + return res; + } + } + for (uint32_t i = 0; i < list_info->capacity / sizeof(struct loader_used_object_status); i++) { + if (list_info->list[i].status == VK_FALSE) { + list_info->list[i].status = VK_TRUE; + if (pAllocator) { + list_info->list[i].allocation_callbacks = *pAllocator; + } else { + memset(&list_info->list[i].allocation_callbacks, 0, sizeof(VkAllocationCallbacks)); + } + *free_index = i; + return VK_SUCCESS; + } + } + // No free space, must resize + + size_t old_capacity = list_info->capacity; + VkResult res = loader_resize_generic_list(inst, (struct loader_generic_list *)list_info); + if (VK_SUCCESS != res) { + return res; + } + uint32_t new_index = (uint32_t)(old_capacity / sizeof(struct loader_used_object_status)); + // Zero out the newly allocated back half of list. + memset(&list_info->list[new_index], 0, old_capacity); + list_info->list[new_index].status = VK_TRUE; + if (pAllocator) { + list_info->list[new_index].allocation_callbacks = *pAllocator; + } else { + memset(&list_info->list[new_index].allocation_callbacks, 0, sizeof(VkAllocationCallbacks)); + } + *free_index = new_index; + return VK_SUCCESS; +} + +void loader_release_object_from_list(struct loader_used_object_list *list_info, uint32_t index_to_free) { + if (list_info->list && list_info->capacity > index_to_free * sizeof(struct loader_used_object_status)) { + list_info->list[index_to_free].status = VK_FALSE; + memset(&list_info->list[index_to_free].allocation_callbacks, 0, sizeof(VkAllocationCallbacks)); + } +} + // Append non-duplicate extension properties defined in props to the given ext_list. // Return - Vk_SUCCESS on success VkResult loader_add_to_ext_list(const struct loader_instance *inst, struct loader_extension_list *ext_list, @@ -923,6 +1005,7 @@ VkResult loader_add_layer_names_to_list(const struct loader_instance *inst, cons // If not a meta-layer, simply add it. if (0 == (layer_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { + layer_prop->enabled_by_what = ENABLED_BY_WHAT_IN_APPLICATION_API; err = loader_add_layer_properties_to_list(inst, output_list, layer_prop); if (err == VK_ERROR_OUT_OF_HOST_MEMORY) return err; err = loader_add_layer_properties_to_list(inst, expanded_output_list, layer_prop); @@ -994,7 +1077,7 @@ bool loader_implicit_layer_is_enabled(const struct loader_instance *inst, const loader_free_getenv(env_value, inst); } else if ((prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER) == 0) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, - "Implicit layer \"%s\" missing disabled environment variable!", prop->info.layerName, VK_LAYERS_DISABLE_ENV_VAR); + "Implicit layer \"%s\" missing disabled environment variable!", prop->info.layerName); } // Enable this layer if it is included in the override layer @@ -1033,7 +1116,7 @@ VkResult loader_add_implicit_layer(const struct loader_instance *inst, struct lo if (loader_find_layer_name_in_list(&prop->info.layerName[0], target_list)) { return result; } - + prop->enabled_by_what = ENABLED_BY_WHAT_IMPLICIT_LAYER; result = loader_add_layer_properties_to_list(inst, target_list, prop); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; if (NULL != expanded_target_list) { @@ -1078,17 +1161,20 @@ VkResult loader_add_meta_layer(const struct loader_instance *inst, const struct // If the component layer is itself an implicit layer, we need to do the implicit layer enable // checks if (0 == (search_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)) { + search_prop->enabled_by_what = ENABLED_BY_WHAT_META_LAYER; result = loader_add_implicit_layer(inst, search_prop, filters, target_list, expanded_target_list, source_list); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; } else { if (0 != (search_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { bool found_layers_in_component_meta_layer = true; + search_prop->enabled_by_what = ENABLED_BY_WHAT_META_LAYER; result = loader_add_meta_layer(inst, filters, search_prop, target_list, expanded_target_list, source_list, &found_layers_in_component_meta_layer); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; if (!found_layers_in_component_meta_layer) found_all_component_layers = false; } else if (!loader_find_layer_name_in_list(&search_prop->info.layerName[0], target_list)) { // Make sure the layer isn't already in the output_list, skip adding it if it is. + search_prop->enabled_by_what = ENABLED_BY_WHAT_META_LAYER; result = loader_add_layer_properties_to_list(inst, target_list, search_prop); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; if (NULL != expanded_target_list) { @@ -1107,6 +1193,7 @@ VkResult loader_add_meta_layer(const struct loader_instance *inst, const struct // Add this layer to the overall target list (not the expanded one) if (found_all_component_layers) { + prop->enabled_by_what = ENABLED_BY_WHAT_META_LAYER; result = loader_add_layer_properties_to_list(inst, target_list, prop); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; // Write the result to out_found_all_component_layers in case this function is being recursed @@ -1232,7 +1319,7 @@ out: return res; } -struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loader_device **found_dev, uint32_t *icd_index) { +struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loader_device **found_dev) { VkLayerDispatchTable *dispatch_table_device = loader_get_dispatch(device); if (NULL == dispatch_table_device) { *found_dev = NULL; @@ -1242,21 +1329,16 @@ struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loa *found_dev = NULL; for (struct loader_instance *inst = loader.instances; inst; inst = inst->next) { - uint32_t index = 0; for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { for (struct loader_device *dev = icd_term->logical_device_list; dev; dev = dev->next) { // Value comparison of device prevents object wrapping by layers if (loader_get_dispatch(dev->icd_device) == dispatch_table_device || (dev->chain_device != VK_NULL_HANDLE && loader_get_dispatch(dev->chain_device) == dispatch_table_device)) { *found_dev = dev; - if (NULL != icd_index) { - *icd_index = index; - } loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock); return icd_term; } } - index++; } } loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock); @@ -1313,15 +1395,59 @@ void loader_remove_logical_device(struct loader_icd_term *icd_term, struct loade loader_destroy_logical_device(found_dev, pAllocator); } +const VkAllocationCallbacks *ignore_null_callback(const VkAllocationCallbacks *callbacks) { + return NULL != callbacks->pfnAllocation && NULL != callbacks->pfnFree && NULL != callbacks->pfnReallocation && + NULL != callbacks->pfnInternalAllocation && NULL != callbacks->pfnInternalFree + ? callbacks + : NULL; +} + +// Try to close any open objects on the loader_icd_term - this must be done before destroying the instance +void loader_icd_close_objects(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term) { + for (uint32_t i = 0; i < icd_term->surface_list.capacity / sizeof(VkSurfaceKHR); i++) { + if (ptr_inst->surfaces_list.capacity > i * sizeof(struct loader_used_object_status) && + ptr_inst->surfaces_list.list[i].status == VK_TRUE && NULL != icd_term->surface_list.list && + icd_term->surface_list.list[i] && NULL != icd_term->dispatch.DestroySurfaceKHR) { + icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, icd_term->surface_list.list[i], + ignore_null_callback(&(ptr_inst->surfaces_list.list[i].allocation_callbacks))); + icd_term->surface_list.list[i] = (VkSurfaceKHR)(uintptr_t)NULL; + } + } + for (uint32_t i = 0; i < icd_term->debug_utils_messenger_list.capacity / sizeof(VkDebugUtilsMessengerEXT); i++) { + if (ptr_inst->debug_utils_messengers_list.capacity > i * sizeof(struct loader_used_object_status) && + ptr_inst->debug_utils_messengers_list.list[i].status == VK_TRUE && NULL != icd_term->debug_utils_messenger_list.list && + icd_term->debug_utils_messenger_list.list[i] && NULL != icd_term->dispatch.DestroyDebugUtilsMessengerEXT) { + icd_term->dispatch.DestroyDebugUtilsMessengerEXT( + icd_term->instance, icd_term->debug_utils_messenger_list.list[i], + ignore_null_callback(&(ptr_inst->debug_utils_messengers_list.list[i].allocation_callbacks))); + icd_term->debug_utils_messenger_list.list[i] = (VkDebugUtilsMessengerEXT)(uintptr_t)NULL; + } + } + for (uint32_t i = 0; i < icd_term->debug_report_callback_list.capacity / sizeof(VkDebugReportCallbackEXT); i++) { + if (ptr_inst->debug_report_callbacks_list.capacity > i * sizeof(struct loader_used_object_status) && + ptr_inst->debug_report_callbacks_list.list[i].status == VK_TRUE && NULL != icd_term->debug_report_callback_list.list && + icd_term->debug_report_callback_list.list[i] && NULL != icd_term->dispatch.DestroyDebugReportCallbackEXT) { + icd_term->dispatch.DestroyDebugReportCallbackEXT( + icd_term->instance, icd_term->debug_report_callback_list.list[i], + ignore_null_callback(&(ptr_inst->debug_report_callbacks_list.list[i].allocation_callbacks))); + icd_term->debug_report_callback_list.list[i] = (VkDebugReportCallbackEXT)(uintptr_t)NULL; + } + } +} +// Free resources allocated inside the loader_icd_term void loader_icd_destroy(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term, const VkAllocationCallbacks *pAllocator) { - ptr_inst->total_icd_count--; + ptr_inst->icd_terms_count--; for (struct loader_device *dev = icd_term->logical_device_list; dev;) { struct loader_device *next_dev = dev->next; loader_destroy_logical_device(dev, pAllocator); dev = next_dev; } + loader_destroy_generic_list(ptr_inst, (struct loader_generic_list *)&icd_term->surface_list); + loader_destroy_generic_list(ptr_inst, (struct loader_generic_list *)&icd_term->debug_utils_messenger_list); + loader_destroy_generic_list(ptr_inst, (struct loader_generic_list *)&icd_term->debug_report_callback_list); + loader_instance_heap_free(ptr_inst, icd_term); } @@ -1339,10 +1465,22 @@ struct loader_icd_term *loader_icd_add(struct loader_instance *ptr_inst, const s // Prepend to the list icd_term->next = ptr_inst->icd_terms; ptr_inst->icd_terms = icd_term; - ptr_inst->total_icd_count++; + ptr_inst->icd_terms_count++; return icd_term; } +// Closes the library handle in the scanned ICD, free the lib_name string, and zeros out all data +void loader_unload_scanned_icd(struct loader_instance *inst, struct loader_scanned_icd *scanned_icd) { + if (NULL == scanned_icd) { + return; + } + if (scanned_icd->handle) { + loader_platform_close_library(scanned_icd->handle); + scanned_icd->handle = NULL; + } + loader_instance_heap_free(inst, scanned_icd->lib_name); + memset(scanned_icd, 0, sizeof(struct loader_scanned_icd)); +} // Determine the ICD interface version to use. // @param icd @@ -1378,7 +1516,7 @@ bool loader_get_icd_interface_version(PFN_vkNegotiateLoaderICDInterfaceVersion f return true; } -void loader_scanned_icd_clear(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) { +void loader_clear_scanned_icd_list(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) { if (0 != icd_tramp_list->capacity && icd_tramp_list->scanned_list) { for (uint32_t i = 0; i < icd_tramp_list->count; i++) { if (icd_tramp_list->scanned_list[i].handle) { @@ -1392,14 +1530,14 @@ void loader_scanned_icd_clear(const struct loader_instance *inst, struct loader_ memset(icd_tramp_list, 0, sizeof(struct loader_icd_tramp_list)); } -VkResult loader_scanned_icd_init(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) { +VkResult loader_init_scanned_icd_list(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) { VkResult res = VK_SUCCESS; - loader_scanned_icd_clear(inst, icd_tramp_list); + loader_clear_scanned_icd_list(inst, icd_tramp_list); icd_tramp_list->capacity = 8 * sizeof(struct loader_scanned_icd); icd_tramp_list->scanned_list = loader_instance_heap_alloc(inst, icd_tramp_list->capacity, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == icd_tramp_list->scanned_list) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "loader_scanned_icd_init: Realloc failed for layer list when attempting to add new layer"); + "loader_init_scanned_icd_list: Realloc failed for layer list when attempting to add new layer"); res = VK_ERROR_OUT_OF_HOST_MEMORY; } return res; @@ -1560,13 +1698,15 @@ VkResult loader_scan_for_direct_drivers(const struct loader_instance *inst, cons } const VkDirectDriverLoadingListLUNARG *ddl_list = NULL; // Find the VkDirectDriverLoadingListLUNARG struct in the pNext chain of vkInstanceCreateInfo - const VkBaseOutStructure *chain = pCreateInfo->pNext; - while (chain) { - if (chain->sType == VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG) { - ddl_list = (VkDirectDriverLoadingListLUNARG *)chain; + const void *pNext = pCreateInfo->pNext; + while (pNext) { + VkBaseInStructure out_structure = {0}; + memcpy(&out_structure, pNext, sizeof(VkBaseInStructure)); + if (out_structure.sType == VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG) { + ddl_list = (VkDirectDriverLoadingListLUNARG *)pNext; break; } - chain = (const VkBaseOutStructure *)chain->pNext; + pNext = out_structure.pNext; } if (NULL == ddl_list) { if (direct_driver_loading_enabled) { @@ -1638,8 +1778,7 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade // This shouldn't happen, but the check is necessary because dlopen returns a handle to the main program when // filename is NULL if (filename == NULL) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: A NULL filename was used, skipping this ICD", - filename); + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: A NULL filename was used, skipping this ICD"); res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } @@ -1825,12 +1964,18 @@ out: return res; } +#if defined(_WIN32) +BOOL __stdcall loader_initialize(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) { + (void)InitOnce; + (void)Parameter; + (void)Context; +#else void loader_initialize(void) { - // initialize mutexes loader_platform_thread_create_mutex(&loader_lock); loader_platform_thread_create_mutex(&loader_preload_icd_lock); loader_platform_thread_create_mutex(&loader_global_instance_list_lock); init_global_loader_settings(); +#endif // initialize logging loader_init_global_debug_level(); @@ -1857,9 +2002,12 @@ void loader_initialize(void) { #if defined(LOADER_USE_UNSAFE_FILE_SEARCH) loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "Vulkan Loader: unsafe searching is enabled"); #endif +#if defined(_WIN32) + return TRUE; +#endif } -void loader_release() { +void loader_release(void) { // Guarantee release of the preloaded ICD libraries. This may have already been called in vkDestroyInstance. loader_unload_preloaded_icds(); @@ -1875,14 +2023,14 @@ void loader_preload_icds(void) { loader_platform_thread_lock_mutex(&loader_preload_icd_lock); // Already preloaded, skip loading again. - if (scanned_icds.scanned_list != NULL) { + if (preloaded_icds.scanned_list != NULL) { loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); return; } - VkResult result = loader_icd_scan(NULL, &scanned_icds, NULL, NULL); + VkResult result = loader_icd_scan(NULL, &preloaded_icds, NULL, NULL); if (result != VK_SUCCESS) { - loader_scanned_icd_clear(NULL, &scanned_icds); + loader_clear_scanned_icd_list(NULL, &preloaded_icds); } loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); } @@ -1890,7 +2038,7 @@ void loader_preload_icds(void) { // Release the ICD libraries that were preloaded void loader_unload_preloaded_icds(void) { loader_platform_thread_lock_mutex(&loader_preload_icd_lock); - loader_scanned_icd_clear(NULL, &scanned_icds); + loader_clear_scanned_icd_list(NULL, &preloaded_icds); loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); } @@ -2019,10 +2167,24 @@ void loader_get_fullpath(const char *file, const char *in_dirs, size_t out_size, } // Verify that all component layers in a meta-layer are valid. -bool verify_meta_layer_component_layers(const struct loader_instance *inst, struct loader_layer_properties *prop, - struct loader_layer_list *instance_layers) { +// This function is potentially recursive so we pass in an array of "already checked" (length of the instance_layers->count) meta +// layers, preventing a stack overflow verifying meta layers that are each other's component layers +bool verify_meta_layer_component_layers(const struct loader_instance *inst, size_t prop_index, + struct loader_layer_list *instance_layers, bool *already_checked_meta_layers) { + struct loader_layer_properties *prop = &instance_layers->list[prop_index]; loader_api_version meta_layer_version = loader_make_version(prop->info.specVersion); + if (NULL == already_checked_meta_layers) { + already_checked_meta_layers = loader_stack_alloc(sizeof(bool) * instance_layers->count); + if (already_checked_meta_layers == NULL) { + return false; + } + memset(already_checked_meta_layers, 0, sizeof(bool) * instance_layers->count); + } + + // Mark this meta layer as 'already checked', indicating which layers have already been recursed. + already_checked_meta_layers[prop_index] = true; + for (uint32_t comp_layer = 0; comp_layer < prop->component_layer_names.count; comp_layer++) { struct loader_layer_properties *comp_prop = loader_find_layer_property(prop->component_layer_names.list[comp_layer], instance_layers); @@ -2057,12 +2219,27 @@ bool verify_meta_layer_component_layers(const struct loader_instance *inst, stru return false; } if (comp_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { + size_t comp_prop_index = INT32_MAX; + // Make sure we haven't verified this meta layer before + for (uint32_t i = 0; i < instance_layers->count; i++) { + if (strcmp(comp_prop->info.layerName, instance_layers->list[i].info.layerName) == 0) { + comp_prop_index = i; + } + } + if (comp_prop_index != INT32_MAX && already_checked_meta_layers[comp_prop_index]) { + loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, + "verify_meta_layer_component_layers: Recursive depedency between Meta-layer %s and Meta-layer %s. " + "Skipping this layer.", + instance_layers->list[prop_index].info.layerName, comp_prop->info.layerName); + return false; + } + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "verify_meta_layer_component_layers: Adding meta-layer %s which also contains meta-layer %s", prop->info.layerName, comp_prop->info.layerName); // Make sure if the layer is using a meta-layer in its component list that we also verify that. - if (!verify_meta_layer_component_layers(inst, comp_prop, instance_layers)) { + if (!verify_meta_layer_component_layers(inst, comp_prop_index, instance_layers, already_checked_meta_layers)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Meta-layer %s component layer %s can not find all component layers." " Skipping this layer.", @@ -2136,7 +2313,7 @@ VkResult verify_all_meta_layers(struct loader_instance *inst, const struct loade // If this is a meta-layer, make sure it is valid if (prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { - if (verify_meta_layer_component_layers(inst, prop, instance_layers)) { + if (verify_meta_layer_component_layers(inst, i, instance_layers, NULL)) { // If any meta layer is valid, update its extension list to include the extensions from its component layers. res = update_meta_layer_extensions_from_component_layers(inst, prop, instance_layers); if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { @@ -2242,16 +2419,18 @@ void remove_all_non_valid_override_layers(struct loader_instance *inst, struct l VkResult loader_read_layer_json(const struct loader_instance *inst, struct loader_layer_list *layer_instance_list, cJSON *layer_node, loader_api_version version, bool is_implicit, char *filename) { assert(layer_instance_list); - char *type = NULL; - char *api_version = NULL; - char *implementation_version = NULL; + char *library_path = NULL; VkResult result = VK_SUCCESS; struct loader_layer_properties props = {0}; + result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); + if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { + goto out; + } + // Parse name - result = loader_parse_json_string_to_existing_str(inst, layer_node, "name", VK_MAX_EXTENSION_NAME_SIZE, props.info.layerName); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; + result = loader_parse_json_string_to_existing_str(layer_node, "name", VK_MAX_EXTENSION_NAME_SIZE, props.info.layerName); if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"name\" in manifest JSON file, skipping this layer", @@ -2270,10 +2449,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } // Parse type - - result = loader_parse_json_string(layer_node, "type", &type); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; - if (VK_ERROR_INITIALIZATION_FAILED == result) { + char *type = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "type")); + if (NULL == type) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"type\" in manifest JSON file, skipping this layer", filename); @@ -2282,7 +2459,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Add list entry if (!strcmp(type, "DEVICE")) { - loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Device layers are deprecated. Skipping this layer"); + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Device layers are deprecated. Skipping layer %s", + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2299,10 +2477,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } // Parse api_version - - result = loader_parse_json_string(layer_node, "api_version", &api_version); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; - if (VK_ERROR_INITIALIZATION_FAILED == result) { + char *api_version = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "api_version")); + if (NULL == api_version) { loader_log( inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"api_version\" in manifest JSON file, skipping this layer", @@ -2323,10 +2499,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } // Parse implementation_version - - result = loader_parse_json_string(layer_node, "implementation_version", &implementation_version); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; - if (VK_ERROR_INITIALIZATION_FAILED == result) { + char *implementation_version = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "implementation_version")); + if (NULL == implementation_version) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer located at %s didn't find required layer value \"implementation_version\" in manifest JSON file, " "skipping this layer", @@ -2337,9 +2511,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Parse description - result = loader_parse_json_string_to_existing_str(inst, layer_node, "description", VK_MAX_EXTENSION_NAME_SIZE, - props.info.description); - if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; + result = + loader_parse_json_string_to_existing_str(layer_node, "description", VK_MAX_EXTENSION_NAME_SIZE, props.info.description); if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log( inst, VULKAN_LOADER_WARN_BIT, 0, @@ -2351,30 +2524,28 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // Parse library_path // Library path no longer required unless component_layers is also not defined - cJSON *library_path = loader_cJSON_GetObjectItem(layer_node, "library_path"); - + result = loader_parse_json_string(layer_node, "library_path", &library_path); + if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { + loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, + "Skipping layer \"%s\" due to problem accessing the library_path value in the manifest JSON file", + props.info.layerName); + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } if (NULL != library_path) { if (NULL != loader_cJSON_GetObjectItem(layer_node, "component_layers")) { - loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Indicating meta-layer-specific component_layers, but also defining layer library path. Both are not " - "compatible, so skipping this layer"); + loader_log( + inst, VULKAN_LOADER_WARN_BIT, 0, + "Layer \"%s\" contains meta-layer-specific component_layers, but also defining layer library path. Both are not " + "compatible, so skipping this layer", + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; - goto out; - } - - result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - - char *library_path_str = loader_cJSON_Print(library_path); - if (NULL == library_path_str) { - loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Skipping layer due to problem accessing the library_path value in manifest JSON file %s", filename); - result = VK_ERROR_OUT_OF_HOST_MEMORY; + loader_instance_heap_free(inst, library_path); goto out; } // This function takes ownership of library_path_str - so we don't need to clean it up - result = combine_manifest_directory_and_library_path(inst, library_path_str, filename, &props.lib_name); + result = combine_manifest_directory_and_library_path(inst, library_path, filename, &props.lib_name); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; } @@ -2383,7 +2554,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (NULL == library_path) { if (!loader_check_version_meets_required(LOADER_VERSION_1_1_0, version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Indicating meta-layer-specific component_layers, but using older JSON file version."); + "Layer \"%s\" contains meta-layer-specific component_layers, but using older JSON file version.", + props.info.layerName); } result = loader_parse_json_array_of_strings(inst, layer_node, "component_layers", &(props.component_layer_names)); @@ -2392,8 +2564,9 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } if (VK_ERROR_INITIALIZATION_FAILED == result) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Layer missing both library_path and component_layers fields. One or the other MUST be defined. Skipping " - "this layer"); + "Layer \"%s\" is missing both library_path and component_layers fields. One or the other MUST be defined. " + "Skipping this layer", + props.info.layerName); goto out; } // This is now, officially, a meta-layer @@ -2419,7 +2592,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade } if (NULL != props.override_paths.list && !loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Indicating meta-layer-specific override paths, but using older JSON file version."); + "Layer \"%s\" contains meta-layer-specific override paths, but using older JSON file version.", + props.info.layerName); } // Parse disable_environment @@ -2428,15 +2602,19 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade cJSON *disable_environment = loader_cJSON_GetObjectItem(layer_node, "disable_environment"); if (disable_environment == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Didn't find required layer object disable_environment in manifest JSON file, skipping this layer"); + "Layer \"%s\" doesn't contain required layer object disable_environment in the manifest JSON file, skipping " + "this layer", + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } - if (!disable_environment->child || disable_environment->child->type != cJSON_String) { + if (!disable_environment->child || disable_environment->child->type != cJSON_String || + !disable_environment->child->string || !disable_environment->child->valuestring) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Didn't find required layer child value disable_environment in manifest JSON file, skipping this layer " - "(Policy #LLP_LAYER_9)"); + "Layer \"%s\" doesn't contain required child value in object disable_environment in the manifest JSON file, " + "skipping this layer (Policy #LLP_LAYER_9)", + props.info.layerName); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } @@ -2467,7 +2645,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade result = loader_parse_json_string(functions, "vkGetInstanceProcAddr", &props.functions.str_gipa); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - if (props.functions.str_gipa && loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { + if (NULL == props.functions.str_negotiate_interface && props.functions.str_gipa && + loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Layer \"%s\" using deprecated \'vkGetInstanceProcAddr\' tag which was deprecated starting with JSON " "file version 1.1.0. The new vkNegotiateLoaderLayerInterfaceVersion function is preferred, though for " @@ -2478,7 +2657,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade result = loader_parse_json_string(functions, "vkGetDeviceProcAddr", &props.functions.str_gdpa); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - if (props.functions.str_gdpa && loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { + if (NULL == props.functions.str_negotiate_interface && props.functions.str_gdpa && + loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Layer \"%s\" using deprecated \'vkGetDeviceProcAddr\' tag which was deprecated starting with JSON " "file version 1.1.0. The new vkNegotiateLoaderLayerInterfaceVersion function is preferred, though for " @@ -2494,15 +2674,18 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // } cJSON *instance_extensions = loader_cJSON_GetObjectItem(layer_node, "instance_extensions"); - if (instance_extensions != NULL) { - int count = loader_cJSON_GetArraySize(instance_extensions); - for (int i = 0; i < count; i++) { + if (instance_extensions != NULL && instance_extensions->type == cJSON_Array) { + cJSON *ext_item = NULL; + cJSON_ArrayForEach(ext_item, instance_extensions) { + if (ext_item->type != cJSON_Object) { + continue; + } + VkExtensionProperties ext_prop = {0}; - cJSON *ext_item = loader_cJSON_GetArrayItem(instance_extensions, i); - result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, - ext_prop.extensionName); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - if (result == VK_ERROR_INITIALIZATION_FAILED) continue; + result = loader_parse_json_string_to_existing_str(ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); + if (result == VK_ERROR_INITIALIZATION_FAILED) { + continue; + } char *spec_version = NULL; result = loader_parse_json_string(ext_item, "spec_version", &spec_version); if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; @@ -2524,16 +2707,18 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade // entrypoints // } cJSON *device_extensions = loader_cJSON_GetObjectItem(layer_node, "device_extensions"); - if (device_extensions != NULL) { - int count = loader_cJSON_GetArraySize(device_extensions); - for (int i = 0; i < count; i++) { - VkExtensionProperties ext_prop = {0}; - - cJSON *ext_item = loader_cJSON_GetArrayItem(device_extensions, i); + if (device_extensions != NULL && device_extensions->type == cJSON_Array) { + cJSON *ext_item = NULL; + cJSON_ArrayForEach(ext_item, device_extensions) { + if (ext_item->type != cJSON_Object) { + continue; + } - result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, - ext_prop.extensionName); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; + VkExtensionProperties ext_prop = {0}; + result = loader_parse_json_string_to_existing_str(ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, ext_prop.extensionName); + if (result == VK_ERROR_INITIALIZATION_FAILED) { + continue; + } char *spec_version = NULL; result = loader_parse_json_string(ext_item, "spec_version", &spec_version); @@ -2561,7 +2746,8 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade cJSON *enable_environment = loader_cJSON_GetObjectItem(layer_node, "enable_environment"); // enable_environment is optional - if (enable_environment && enable_environment->child && enable_environment->child->type == cJSON_String) { + if (enable_environment && enable_environment->child && enable_environment->child->type == cJSON_String && + enable_environment->child->string && enable_environment->child->valuestring) { result = loader_copy_to_new_str(inst, enable_environment->child->string, &(props.enable_env_var.name)); if (VK_SUCCESS != result) goto out; result = loader_copy_to_new_str(inst, enable_environment->child->valuestring, &(props.enable_env_var.value)); @@ -2610,19 +2796,16 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; } - char *library_arch = NULL; - result = loader_parse_json_string(layer_node, "library_arch", &library_arch); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; - if (library_arch != NULL) { + char *library_arch = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(layer_node, "library_arch")); + if (NULL != library_arch) { if ((strncmp(library_arch, "32", 2) == 0 && sizeof(void *) != 4) || (strncmp(library_arch, "64", 2) == 0 && sizeof(void *) != 8)) { loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, - "Layer library architecture doesn't match the current running architecture, skipping this layer"); - loader_instance_heap_free(inst, library_arch); + "The library architecture in layer %s doesn't match the current running architecture, skipping this layer", + filename); result = VK_ERROR_INITIALIZATION_FAILED; goto out; } - loader_instance_heap_free(inst, library_arch); } result = VK_SUCCESS; @@ -2636,9 +2819,6 @@ out: if (VK_SUCCESS != result) { loader_free_layer_properties(inst, &props); } - loader_instance_heap_free(inst, type); - loader_instance_heap_free(inst, api_version); - loader_instance_heap_free(inst, implementation_version); return result; } @@ -2668,25 +2848,20 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct // - If more than one "layer" object are used, then the "layers" array is // required VkResult result = VK_ERROR_INITIALIZATION_FAILED; - cJSON *item, *layers_node, *layer_node; - loader_api_version json_version = {0, 0, 0}; - char *file_vers = NULL; // Make sure sure the top level json value is an object - if (!json || json->type != 6) { + if (!json || json->type != cJSON_Object) { goto out; } - item = loader_cJSON_GetObjectItem(json, "file_format_version"); - if (item == NULL) { - goto out; - } - file_vers = loader_cJSON_PrintUnformatted(item); + char *file_vers = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(json, "file_format_version")); if (NULL == file_vers) { - result = VK_ERROR_OUT_OF_HOST_MEMORY; + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "loader_add_layer_properties: Manifest %s missing required field file_format_version", filename); goto out; } + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Found manifest file %s (file version %s)", filename, file_vers); // Get the major/minor/and patch as integers for easier comparison - json_version = loader_make_full_version(loader_parse_version_string(file_vers)); + loader_api_version json_version = loader_make_full_version(loader_parse_version_string(file_vers)); if (!is_valid_layer_json_version(&json_version)) { loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, @@ -2695,9 +2870,8 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct } // If "layers" is present, read in the array of layer objects - layers_node = loader_cJSON_GetObjectItem(json, "layers"); + cJSON *layers_node = loader_cJSON_GetObjectItem(json, "layers"); if (layers_node != NULL) { - int numItems = loader_cJSON_GetArraySize(layers_node); // Supported versions started in 1.0.1, so anything newer if (!loader_check_version_meets_required(loader_combine_version(1, 0, 1), json_version)) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, @@ -2705,24 +2879,29 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct "version %s", filename, file_vers); } - for (int curLayer = 0; curLayer < numItems; curLayer++) { - layer_node = loader_cJSON_GetArrayItem(layers_node, curLayer); - if (layer_node == NULL) { - loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, - "loader_add_layer_properties: Can not find 'layers' array element %d object in manifest JSON file %s. " - "Skipping this file", - curLayer, filename); + cJSON *layer_node = NULL; + cJSON_ArrayForEach(layer_node, layers_node) { + if (layer_node->type != cJSON_Object) { + loader_log( + inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "loader_add_layer_properties: Array element in \"layers\" field in manifest JSON file %s is not an object. " + "Skipping this file", + filename); goto out; } result = loader_read_layer_json(inst, layer_instance_list, layer_node, json_version, is_implicit, filename); } } else { // Otherwise, try to read in individual layers - layer_node = loader_cJSON_GetObjectItem(json, "layer"); + cJSON *layer_node = loader_cJSON_GetObjectItem(json, "layer"); if (layer_node == NULL) { - loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, - "loader_add_layer_properties: Can not find 'layer' object in manifest JSON file %s. Skipping this file.", - filename); + // Don't warn if this happens to be an ICD manifest + if (loader_cJSON_GetObjectItem(json, "ICD") == NULL) { + loader_log( + inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "loader_add_layer_properties: Can not find 'layer' object in manifest JSON file %s. Skipping this file.", + filename); + } goto out; } // Loop through all "layer" objects in the file to get a count of them @@ -2751,7 +2930,6 @@ VkResult loader_add_layer_properties(const struct loader_instance *inst, struct } out: - loader_instance_heap_free(inst, file_vers); return result; } @@ -2844,21 +3022,16 @@ out: VkResult add_data_files(const struct loader_instance *inst, char *search_path, struct loader_string_list *out_files, bool use_first_found_manifest) { VkResult vk_result = VK_SUCCESS; - DIR *dir_stream = NULL; - struct dirent *dir_entry; - char *cur_file; - char *next_file; - char *name; char full_path[2048]; #if !defined(_WIN32) char temp_path[2048]; #endif // Now, parse the paths - next_file = search_path; + char *next_file = search_path; while (NULL != next_file && *next_file != '\0') { - name = NULL; - cur_file = next_file; + char *name = NULL; + char *cur_file = next_file; next_file = loader_get_next_path(cur_file); // Is this a JSON file, then try to open it. @@ -2897,12 +3070,19 @@ VkResult add_data_files(const struct loader_instance *inst, char *search_path, s break; } } else { // Otherwise, treat it as a directory - dir_stream = loader_opendir(inst, cur_file); + DIR *dir_stream = loader_opendir(inst, cur_file); if (NULL == dir_stream) { continue; } while (1) { - dir_entry = readdir(dir_stream); + errno = 0; + struct dirent *dir_entry = readdir(dir_stream); +#if !defined(WIN32) // Windows doesn't use readdir, don't check errors on functions which aren't called + if (errno != 0) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "readdir failed with %d: %s", errno, strerror(errno)); + break; + } +#endif if (NULL == dir_entry) { break; } @@ -2950,8 +3130,8 @@ VkResult read_data_files_in_search_paths(const struct loader_instance *inst, enu char *cur_path_ptr = NULL; bool use_first_found_manifest = false; #if COMMON_UNIX_PLATFORMS - char *relative_location = NULL; // Only used on unix platforms - size_t rel_size = 0; // unused in windows, dont declare so no compiler warnings are generated + const char *relative_location = NULL; // Only used on unix platforms + size_t rel_size = 0; // unused in windows, dont declare so no compiler warnings are generated #endif #if defined(_WIN32) @@ -3084,13 +3264,18 @@ VkResult read_data_files_in_search_paths(const struct loader_instance *inst, enu #endif break; case LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER: + override_env = loader_secure_getenv(VK_IMPLICIT_LAYER_PATH_ENV_VAR, inst); + additional_env = loader_secure_getenv(VK_ADDITIONAL_IMPLICIT_LAYER_PATH_ENV_VAR, inst); #if COMMON_UNIX_PLATFORMS relative_location = VK_ILAYERS_INFO_RELATIVE_DIR; +#endif +#if defined(_WIN32) + package_path = windows_get_app_package_manifest_path(inst); #endif break; case LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER: - override_env = loader_secure_getenv(VK_LAYER_PATH_ENV_VAR, inst); - additional_env = loader_secure_getenv(VK_ADDITIONAL_LAYER_PATH_ENV_VAR, inst); + override_env = loader_secure_getenv(VK_EXPLICIT_LAYER_PATH_ENV_VAR, inst); + additional_env = loader_secure_getenv(VK_ADDITIONAL_EXPLICIT_LAYER_PATH_ENV_VAR, inst); #if COMMON_UNIX_PLATFORMS relative_location = VK_ELAYERS_INFO_RELATIVE_DIR; #endif @@ -3461,26 +3646,23 @@ struct ICDManifestInfo { VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *file_str, struct ICDManifestInfo *icd, bool *skipped_portability_drivers) { VkResult res = VK_SUCCESS; - cJSON *json = NULL; - char *file_vers_str = NULL; - char *library_arch_str = NULL; - char *version_str = NULL; + cJSON *icd_manifest_json = NULL; if (file_str == NULL) { goto out; } - res = loader_get_json(inst, file_str, &json); + res = loader_get_json(inst, file_str, &icd_manifest_json); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { goto out; } - if (res != VK_SUCCESS || NULL == json) { + if (res != VK_SUCCESS || NULL == icd_manifest_json) { res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - cJSON *item = loader_cJSON_GetObjectItem(json, "file_format_version"); - if (item == NULL) { + cJSON *file_format_version_json = loader_cJSON_GetObjectItem(icd_manifest_json, "file_format_version"); + if (file_format_version_json == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s does not have a \'file_format_version\' field. Skipping ICD JSON.", file_str); @@ -3488,13 +3670,12 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - file_vers_str = loader_cJSON_Print(item); + char *file_vers_str = loader_cJSON_GetStringValue(file_format_version_json); if (NULL == file_vers_str) { // Only reason the print can fail is if there was an allocation issue loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'file_format_version\' field. Skipping ICD JSON", file_str); - res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } loader_log(inst, VULKAN_LOADER_DRIVER_BIT, 0, "Found ICD manifest file %s, version %s", file_str, file_vers_str); @@ -3509,34 +3690,38 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil json_file_version.major, json_file_version.minor, json_file_version.patch); } - cJSON *itemICD = loader_cJSON_GetObjectItem(json, "ICD"); + cJSON *itemICD = loader_cJSON_GetObjectItem(icd_manifest_json, "ICD"); if (itemICD == NULL) { - loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "loader_parse_icd_manifest: Can not find \'ICD\' object in ICD JSON file %s. Skipping ICD JSON", file_str); + // Don't warn if this happens to be a layer manifest file + if (loader_cJSON_GetObjectItem(icd_manifest_json, "layer") == NULL && + loader_cJSON_GetObjectItem(icd_manifest_json, "layers") == NULL) { + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, + "loader_parse_icd_manifest: Can not find \'ICD\' object in ICD JSON file %s. Skipping ICD JSON", file_str); + } res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - item = loader_cJSON_GetObjectItem(itemICD, "library_path"); - if (item == NULL) { + cJSON *library_path_json = loader_cJSON_GetObjectItem(itemICD, "library_path"); + if (library_path_json == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed to find \'library_path\' object in ICD JSON file %s. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - char *library_path = loader_cJSON_Print(item); - if (!library_path) { + bool out_of_memory = false; + char *library_path = loader_cJSON_Print(library_path_json, &out_of_memory); + if (out_of_memory) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'library_path\' field. Skipping ICD JSON.", file_str); res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; - } - - if (strlen(library_path) == 0) { + } else if (!library_path || strlen(library_path) == 0) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s \'library_path\' field is empty. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; + loader_instance_heap_free(inst, library_path); goto out; } @@ -3548,20 +3733,19 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - item = loader_cJSON_GetObjectItem(itemICD, "api_version"); - if (item == NULL) { + cJSON *api_version_json = loader_cJSON_GetObjectItem(itemICD, "api_version"); + if (api_version_json == NULL) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: ICD JSON %s does not have an \'api_version\' field. Skipping ICD JSON.", file_str); res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } - version_str = loader_cJSON_Print(item); + char *version_str = loader_cJSON_GetStringValue(api_version_json); if (NULL == version_str) { // Only reason the print can fail is if there was an allocation issue loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, "loader_parse_icd_manifest: Failed retrieving ICD JSON %s \'api_version\' field. Skipping ICD JSON.", file_str); - res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; } icd->version = loader_parse_version_string(version_str); @@ -3577,8 +3761,8 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil // Skip over ICD's which contain a true "is_portability_driver" value whenever the application doesn't enable // portability enumeration. - item = loader_cJSON_GetObjectItem(itemICD, "is_portability_driver"); - if (item != NULL && item->type == cJSON_True && inst && !inst->portability_enumeration_enabled) { + cJSON *is_portability_driver_json = loader_cJSON_GetObjectItem(itemICD, "is_portability_driver"); + if (loader_cJSON_IsTrue(is_portability_driver_json) && inst && !inst->portability_enumeration_enabled) { if (skipped_portability_drivers) { *skipped_portability_drivers = true; } @@ -3586,29 +3770,20 @@ VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *fil goto out; } - item = loader_cJSON_GetObjectItem(itemICD, "library_arch"); - if (item != NULL) { - library_arch_str = loader_cJSON_Print(item); - if (NULL != library_arch_str) { - // cJSON includes the quotes by default, so we need to look for those here - if ((strncmp(library_arch_str, "32", 4) == 0 && sizeof(void *) != 4) || - (strncmp(library_arch_str, "64", 4) == 0 && sizeof(void *) != 8)) { - loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, - "loader_parse_icd_manifest: Driver library architecture doesn't match the current running " - "architecture, skipping this driver"); - res = VK_ERROR_INCOMPATIBLE_DRIVER; - goto out; - } - } else { - res = VK_ERROR_OUT_OF_HOST_MEMORY; + char *library_arch_str = loader_cJSON_GetStringValue(loader_cJSON_GetObjectItem(itemICD, "library_arch")); + if (library_arch_str != NULL) { + // cJSON includes the quotes by default, so we need to look for those here + if ((strncmp(library_arch_str, "32", 4) == 0 && sizeof(void *) != 4) || + (strncmp(library_arch_str, "64", 4) == 0 && sizeof(void *) != 8)) { + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, + "loader_parse_icd_manifest: Driver library architecture doesn't match the current running " + "architecture, skipping this driver"); + res = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } } out: - loader_cJSON_Delete(json); - loader_instance_heap_free(inst, file_vers_str); - loader_instance_heap_free(inst, version_str); - loader_instance_heap_free(inst, library_arch_str); + loader_cJSON_Delete(icd_manifest_json); return res; } @@ -3635,7 +3810,7 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t struct ICDManifestInfo *icd_details = NULL; // Set up the ICD Trampoline list so elements can be written into it. - res = loader_scanned_icd_init(inst, icd_tramp_list); + res = loader_init_scanned_icd_list(inst, icd_tramp_list); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { return res; } @@ -3926,6 +4101,17 @@ VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct lo goto out; } + // Remove layers from settings file that are off, are explicit, or are implicit layers that aren't active + for (uint32_t i = 0; i < settings_layers.count; ++i) { + if (settings_layers.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_OFF || + settings_layers.list[i].settings_control_value == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION || + (settings_layers.list[i].type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER) == VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER || + !loader_implicit_layer_is_enabled(inst, layer_filters, &settings_layers.list[i])) { + loader_remove_layer_in_list(inst, &settings_layers, i); + i--; + } + } + // If we should not look for layers using other mechanisms, assign settings_layers to instance_layers and jump to the // output if (!should_search_for_other_layers) { @@ -4042,6 +4228,17 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL loader_gpa_instance_terminator(VkInstan if (!strcmp(pName, "vkCreateInstance")) { return (PFN_vkVoidFunction)terminator_CreateInstance; } + // If a layer is querying pre-instance functions using vkGetInstanceProcAddr, we need to return function pointers that match the + // Vulkan API + if (!strcmp(pName, "vkEnumerateInstanceLayerProperties")) { + return (PFN_vkVoidFunction)terminator_EnumerateInstanceLayerProperties; + } + if (!strcmp(pName, "vkEnumerateInstanceExtensionProperties")) { + return (PFN_vkVoidFunction)terminator_EnumerateInstanceExtensionProperties; + } + if (!strcmp(pName, "vkEnumerateInstanceVersion")) { + return (PFN_vkVoidFunction)terminator_EnumerateInstanceVersion; + } // While the spec is very clear that querying vkCreateDevice requires a valid VkInstance, because the loader allowed querying // with a NULL VkInstance handle for a long enough time, it is impractical to fix this bug in the loader @@ -4129,7 +4326,7 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL loader_gpa_instance_terminator(VkInstan VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL loader_gpa_device_terminator(VkDevice device, const char *pName) { struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, NULL); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); // Return this function if a layer above here is asking for the vkGetDeviceProcAddr. // This is so we can properly intercept any device commands needing a terminator. @@ -4382,9 +4579,14 @@ bool loader_get_layer_interface_version(PFN_vkNegotiateLoaderLayerInterfaceVersi void setup_logical_device_enabled_layer_extensions(const struct loader_instance *inst, struct loader_device *dev, const struct loader_extension_list *icd_exts, const VkDeviceCreateInfo *pCreateInfo) { + // no enabled extensions, early exit + if (pCreateInfo->ppEnabledExtensionNames == NULL) { + return; + } // Can only setup debug marker as debug utils is an instance extensions. for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; ++i) { - if (!strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { + if (pCreateInfo->ppEnabledExtensionNames[i] && + !strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { // Check if its supported by the driver for (uint32_t j = 0; j < icd_exts->count; ++j) { if (!strcmp(icd_exts->list[j].extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { @@ -4524,7 +4726,7 @@ VKAPI_ATTR void VKAPI_CALL loader_layer_destroy_device(VkDevice device, const Vk return; } - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, NULL); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); destroyFunction(device, pAllocator); if (NULL != dev) { @@ -4697,8 +4899,11 @@ VkResult loader_create_instance_chain(const VkInstanceCreateInfo *pCreateInfo, c activated_layers[num_activated_layers].manifest = layer_prop->manifest_file_name; activated_layers[num_activated_layers].library = layer_prop->lib_name; activated_layers[num_activated_layers].is_implicit = !(layer_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER); + activated_layers[num_activated_layers].enabled_by_what = layer_prop->enabled_by_what; if (activated_layers[num_activated_layers].is_implicit) { activated_layers[num_activated_layers].disable_env = layer_prop->disable_env_var.name; + activated_layers[num_activated_layers].enable_name_env = layer_prop->enable_env_var.name; + activated_layers[num_activated_layers].enable_value_env = layer_prop->enable_env_var.value; } loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Insert instance layer \"%s\" (%s)", @@ -4770,6 +4975,11 @@ VkResult loader_create_instance_chain(const VkInstanceCreateInfo *pCreateInfo, c feature_flags = windows_initialize_dxgi(); #endif + // The following line of code is actually invalid at least according to the Vulkan spec with header update 1.2.193 and onwards. + // The update required calls to vkGetInstanceProcAddr querying "global" functions (which includes vkCreateInstance) to pass NULL + // for the instance parameter. Because it wasn't required to be NULL before, there may be layers which expect the loader's + // behavior of passing a non-NULL value into vkGetInstanceProcAddr. + // In an abundance of caution, the incorrect code remains as is, with a big comment to indicate that its wrong PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)next_gipa(*created_instance, "vkCreateInstance"); if (fpCreateInstance) { VkLayerInstanceCreateInfo instance_dispatch; @@ -4805,9 +5015,16 @@ VkResult loader_create_instance_chain(const VkInstanceCreateInfo *pCreateInfo, c loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " %s", activated_layers[index].name); loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Type: %s", activated_layers[index].is_implicit ? "Implicit" : "Explicit"); + loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Enabled By: %s", + get_enabled_by_what_str(activated_layers[index].enabled_by_what)); if (activated_layers[index].is_implicit) { loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Disable Env Var: %s", activated_layers[index].disable_env); + if (activated_layers[index].enable_name_env) { + loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, + " This layer was enabled because Env Var %s was set to Value %s", + activated_layers[index].enable_name_env, activated_layers[index].enable_value_env); + } } loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Manifest: %s", activated_layers[index].manifest); loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Library: %s", activated_layers[index].library); @@ -5034,6 +5251,7 @@ VkResult loader_create_device_chain(const VkPhysicalDevice pd, const VkDeviceCre activated_layers[num_activated_layers].manifest = layer_prop->manifest_file_name; activated_layers[num_activated_layers].library = layer_prop->lib_name; activated_layers[num_activated_layers].is_implicit = !(layer_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER); + activated_layers[num_activated_layers].enabled_by_what = layer_prop->enabled_by_what; if (activated_layers[num_activated_layers].is_implicit) { activated_layers[num_activated_layers].disable_env = layer_prop->disable_env_var.name; } @@ -5068,6 +5286,8 @@ VkResult loader_create_device_chain(const VkPhysicalDevice pd, const VkDeviceCre loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " %s", activated_layers[index].name); loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Type: %s", activated_layers[index].is_implicit ? "Implicit" : "Explicit"); + loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Enabled By: %s", + get_enabled_by_what_str(activated_layers[index].enabled_by_what)); if (activated_layers[index].is_implicit) { loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " Disable Env Var: %s", activated_layers[index].disable_env); @@ -5287,7 +5507,14 @@ out: VkResult loader_validate_device_extensions(struct loader_instance *this_instance, const struct loader_pointer_layer_list *activated_device_layers, const struct loader_extension_list *icd_exts, const VkDeviceCreateInfo *pCreateInfo) { + // Early out to prevent nullptr dereference + if (pCreateInfo->enabledExtensionCount == 0 || pCreateInfo->ppEnabledExtensionNames == NULL) { + return VK_SUCCESS; + } for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { + if (pCreateInfo->ppEnabledExtensionNames[i] == NULL) { + continue; + } VkStringErrorFlags result = vk_string_validate(MaxLoaderStringLength, pCreateInfo->ppEnabledExtensionNames[i]); if (result != VK_STRING_ERROR_NONE) { loader_log(this_instance, VULKAN_LOADER_ERROR_BIT, 0, @@ -5344,7 +5571,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI "#LLP_LAYER_21)"); } else if (LOADER_MAGIC_NUMBER != ptr_instance->magic) { loader_log(ptr_instance, VULKAN_LOADER_WARN_BIT, 0, - "terminator_CreateInstance: Instance pointer (%p) has invalid MAGIC value 0x%08lx. Instance value possibly " + "terminator_CreateInstance: Instance pointer (%p) has invalid MAGIC value 0x%08" PRIx64 + ". Instance value possibly " "corrupted by active layer (Policy #LLP_LAYER_21). ", ptr_instance, ptr_instance->magic); } @@ -5467,14 +5695,18 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI #endif // LOADER_ENABLE_LINUX_SORT // Determine if vkGetPhysicalDeviceProperties2 is available to this Instance + // Also determine if VK_EXT_surface_maintenance1 is available on the ICD if (icd_term->scanned_icd->api_version >= VK_API_VERSION_1_1) { icd_term->supports_get_dev_prop_2 = true; - } else { - for (uint32_t j = 0; j < icd_create_info.enabledExtensionCount; j++) { - if (!strcmp(filtered_extension_names[j], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { - icd_term->supports_get_dev_prop_2 = true; - break; - } + } + for (uint32_t j = 0; j < icd_create_info.enabledExtensionCount; j++) { + if (!strcmp(filtered_extension_names[j], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { + icd_term->supports_get_dev_prop_2 = true; + continue; + } + if (!strcmp(filtered_extension_names[j], VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME)) { + icd_term->supports_ext_surface_maintenance_1 = true; + continue; } } @@ -5638,6 +5870,7 @@ out: icd_term = ptr_instance->icd_terms; ptr_instance->icd_terms = icd_term->next; if (NULL != icd_term->instance) { + loader_icd_close_objects(ptr_instance, icd_term); icd_term->dispatch.DestroyInstance(icd_term->instance, pAllocator); } loader_icd_destroy(ptr_instance, icd_term, pAllocator); @@ -5663,8 +5896,6 @@ VKAPI_ATTR void VKAPI_CALL terminator_DestroyInstance(VkInstance instance, const if (NULL == ptr_instance) { return; } - struct loader_icd_term *icd_terms = ptr_instance->icd_terms; - struct loader_icd_term *next_icd_term; // Remove this instance from the list of instances: struct loader_instance *prev = NULL; @@ -5684,18 +5915,20 @@ VKAPI_ATTR void VKAPI_CALL terminator_DestroyInstance(VkInstance instance, const } loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock); + struct loader_icd_term *icd_terms = ptr_instance->icd_terms; while (NULL != icd_terms) { if (icd_terms->instance) { + loader_icd_close_objects(ptr_instance, icd_terms); icd_terms->dispatch.DestroyInstance(icd_terms->instance, pAllocator); } - next_icd_term = icd_terms->next; + struct loader_icd_term *next_icd_term = icd_terms->next; icd_terms->instance = VK_NULL_HANDLE; loader_icd_destroy(ptr_instance, icd_terms, pAllocator); icd_terms = next_icd_term; } - loader_scanned_icd_clear(ptr_instance, &ptr_instance->icd_tramp_list); + loader_clear_scanned_icd_list(ptr_instance, &ptr_instance->icd_tramp_list); loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->ext_list); if (NULL != ptr_instance->phys_devs_term) { for (uint32_t i = 0; i < ptr_instance->phys_dev_count_term; i++) { @@ -5742,7 +5975,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical "#LLP_LAYER_22)"); } else if (DEVICE_DISP_TABLE_MAGIC_NUMBER != dev->loader_dispatch.core_dispatch.magic) { loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0, - "terminator_CreateDevice: Device pointer (%p) has invalid MAGIC value 0x%08lx. The expected value is " + "terminator_CreateDevice: Device pointer (%p) has invalid MAGIC value 0x%08" PRIx64 + ". The expected value is " "0x10ADED040410ADED. Device value possibly " "corrupted by active layer (Policy #LLP_LAYER_22). ", dev, dev->loader_dispatch.core_dispatch.magic); @@ -5795,7 +6029,13 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical } for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { + if (pCreateInfo->ppEnabledExtensionNames == NULL) { + continue; + } const char *extension_name = pCreateInfo->ppEnabledExtensionNames[i]; + if (extension_name == NULL) { + continue; + } VkExtensionProperties *prop = get_extension_property(extension_name, &icd_exts); if (prop) { filtered_extension_names[localCreateInfo.enabledExtensionCount] = (char *)extension_name; @@ -5862,7 +6102,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical { const void *pNext = localCreateInfo.pNext; while (pNext != NULL) { - switch (*(VkStructureType *)pNext) { + VkBaseInStructure pNext_in_structure = {0}; + memcpy(&pNext_in_structure, pNext, sizeof(VkBaseInStructure)); + switch (pNext_in_structure.sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { const VkPhysicalDeviceFeatures2KHR *features = pNext; @@ -5914,8 +6156,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical // Multiview properties are also allowed, but since VK_KHX_multiview is a device extension, we'll just let the // ICD handle that error when the user enables the extension here default: { - const VkBaseInStructure *header = pNext; - pNext = header->pNext; + pNext = pNext_in_structure.pNext; break; } } @@ -5927,7 +6168,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical { const void *pNext = localCreateInfo.pNext; while (pNext != NULL) { - switch (*(VkStructureType *)pNext) { + VkBaseInStructure pNext_in_structure = {0}; + memcpy(&pNext_in_structure, pNext, sizeof(VkBaseInStructure)); + switch (pNext_in_structure.sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR: { const VkPhysicalDeviceMaintenance5FeaturesKHR *maintenance_features = pNext; if (maintenance_features->maintenance5 == VK_TRUE) { @@ -5938,8 +6181,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical } default: { - const VkBaseInStructure *header = pNext; - pNext = header->pNext; + pNext = pNext_in_structure.pNext; break; } } @@ -5957,8 +6199,10 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical dev->driver_extensions.khr_device_group_enabled = true; } else if (!strcmp(localCreateInfo.ppEnabledExtensionNames[i], VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { dev->driver_extensions.ext_debug_marker_enabled = true; - } else if (!strcmp(localCreateInfo.ppEnabledExtensionNames[i], "VK_EXT_full_screen_exclusive")) { +#if defined(VK_USE_PLATFORM_WIN32_KHR) + } else if (!strcmp(localCreateInfo.ppEnabledExtensionNames[i], VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME)) { dev->driver_extensions.ext_full_screen_exclusive_enabled = true; +#endif } else if (!strcmp(localCreateInfo.ppEnabledExtensionNames[i], VK_KHR_MAINTENANCE_5_EXTENSION_NAME) && maintenance5_feature_enabled) { dev->should_ignore_device_commands_from_newer_version = true; @@ -5969,10 +6213,14 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDevice(VkPhysicalDevice physical VkPhysicalDeviceProperties properties; icd_term->dispatch.GetPhysicalDeviceProperties(phys_dev_term->phys_dev, &properties); - if (!dev->driver_extensions.khr_device_group_enabled) { - if (properties.apiVersion >= VK_API_VERSION_1_1) { - dev->driver_extensions.khr_device_group_enabled = true; - } + if (properties.apiVersion >= VK_API_VERSION_1_1) { + dev->driver_extensions.version_1_1_enabled = true; + } + if (properties.apiVersion >= VK_API_VERSION_1_2) { + dev->driver_extensions.version_1_2_enabled = true; + } + if (properties.apiVersion >= VK_API_VERSION_1_3) { + dev->driver_extensions.version_1_3_enabled = true; } loader_log(icd_term->this_instance, VULKAN_LOADER_LAYER_BIT | VULKAN_LOADER_DRIVER_BIT, 0, @@ -6247,7 +6495,6 @@ VkResult check_and_add_to_new_phys_devs(struct loader_instance *inst, VkPhysical loader_set_dispatch((void *)new_phys_devs[idx], inst->disp); new_phys_devs[idx]->this_icd_term = dev_array->icd_term; - new_phys_devs[idx]->icd_index = (uint8_t)(dev_array->icd_index); new_phys_devs[idx]->phys_dev = physical_device; // Increment the count of new physical devices @@ -6269,7 +6516,6 @@ VkResult check_and_add_to_new_phys_devs(struct loader_instance *inst, VkPhysical VkResult setup_loader_term_phys_devs(struct loader_instance *inst) { VkResult res = VK_SUCCESS; struct loader_icd_term *icd_term; - uint32_t icd_idx = 0; uint32_t windows_sorted_devices_count = 0; struct loader_icd_physical_devices *windows_sorted_devices_array = NULL; uint32_t icd_count = 0; @@ -6286,7 +6532,7 @@ VkResult setup_loader_term_phys_devs(struct loader_instance *inst) { } #endif - icd_count = inst->total_icd_count; + icd_count = inst->icd_terms_count; // Allocate something to store the physical device characteristics that we read from each ICD. icd_phys_dev_array = @@ -6303,32 +6549,53 @@ VkResult setup_loader_term_phys_devs(struct loader_instance *inst) { // For each ICD, query the number of physical devices, and then get an // internal value for those physical devices. icd_term = inst->icd_terms; + uint32_t icd_idx = 0; while (NULL != icd_term) { res = icd_term->dispatch.EnumeratePhysicalDevices(icd_term->instance, &icd_phys_dev_array[icd_idx].device_count, NULL); - if (VK_SUCCESS != res) { + if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "setup_loader_term_phys_devs: Call to ICD %d's \'vkEnumeratePhysicalDevices\' failed with error 0x%08x", - icd_idx, res); + "setup_loader_term_phys_devs: Call to \'vkEnumeratePhysicalDevices\' in ICD %s failed with error code " + "VK_ERROR_OUT_OF_HOST_MEMORY", + icd_term->scanned_icd->lib_name); goto out; - } + } else if (VK_SUCCESS == res) { + icd_phys_dev_array[icd_idx].physical_devices = + (VkPhysicalDevice *)loader_stack_alloc(icd_phys_dev_array[icd_idx].device_count * sizeof(VkPhysicalDevice)); + if (NULL == icd_phys_dev_array[icd_idx].physical_devices) { + loader_log( + inst, VULKAN_LOADER_ERROR_BIT, 0, + "setup_loader_term_phys_devs: Failed to allocate temporary ICD Physical device array for ICD %s of size %d", + icd_term->scanned_icd->lib_name, icd_phys_dev_array[icd_idx].device_count); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } - icd_phys_dev_array[icd_idx].physical_devices = - (VkPhysicalDevice *)loader_stack_alloc(icd_phys_dev_array[icd_idx].device_count * sizeof(VkPhysicalDevice)); - if (NULL == icd_phys_dev_array[icd_idx].physical_devices) { + res = icd_term->dispatch.EnumeratePhysicalDevices(icd_term->instance, &(icd_phys_dev_array[icd_idx].device_count), + icd_phys_dev_array[icd_idx].physical_devices); + if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, + "setup_loader_term_phys_devs: Call to \'vkEnumeratePhysicalDevices\' in ICD %s failed with error code " + "VK_ERROR_OUT_OF_HOST_MEMORY", + icd_term->scanned_icd->lib_name); + goto out; + } + if (VK_SUCCESS != res) { + loader_log( + inst, VULKAN_LOADER_ERROR_BIT, 0, + "setup_loader_term_phys_devs: Call to \'vkEnumeratePhysicalDevices\' in ICD %s failed with error code %d", + icd_term->scanned_icd->lib_name, res); + icd_phys_dev_array[icd_idx].device_count = 0; + icd_phys_dev_array[icd_idx].physical_devices = 0; + } + } else { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "setup_loader_term_phys_devs: Failed to allocate temporary ICD Physical device array for ICD %d of size %d", - icd_idx, icd_phys_dev_array[icd_idx].device_count); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - - res = icd_term->dispatch.EnumeratePhysicalDevices(icd_term->instance, &(icd_phys_dev_array[icd_idx].device_count), - icd_phys_dev_array[icd_idx].physical_devices); - if (VK_SUCCESS != res) { - goto out; + "setup_loader_term_phys_devs: Call to \'vkEnumeratePhysicalDevices\' in ICD %s failed with error code %d", + icd_term->scanned_icd->lib_name, res); + icd_phys_dev_array[icd_idx].device_count = 0; + icd_phys_dev_array[icd_idx].physical_devices = 0; } icd_phys_dev_array[icd_idx].icd_term = icd_term; - icd_phys_dev_array[icd_idx].icd_index = icd_idx; + icd_term->physical_device_count = icd_phys_dev_array[icd_idx].device_count; icd_term = icd_term->next; ++icd_idx; } @@ -6493,6 +6760,79 @@ out: return res; } +/** + * Iterates through all drivers and unloads any which do not contain physical devices. + * This saves address space, which for 32 bit applications is scarce. + * This must only be called after a call to vkEnumeratePhysicalDevices that isn't just querying the count + */ +void unload_drivers_without_physical_devices(struct loader_instance *inst) { + struct loader_icd_term *cur_icd_term = inst->icd_terms; + struct loader_icd_term *prev_icd_term = NULL; + + while (NULL != cur_icd_term) { + struct loader_icd_term *next_icd_term = cur_icd_term->next; + if (cur_icd_term->physical_device_count == 0) { + uint32_t cur_scanned_icd_index = UINT32_MAX; + if (inst->icd_tramp_list.scanned_list) { + for (uint32_t i = 0; i < inst->icd_tramp_list.count; i++) { + if (&(inst->icd_tramp_list.scanned_list[i]) == cur_icd_term->scanned_icd) { + cur_scanned_icd_index = i; + break; + } + } + } + if (cur_scanned_icd_index != UINT32_MAX) { + loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, + "Removing driver %s due to not having any physical devices", cur_icd_term->scanned_icd->lib_name); + + const VkAllocationCallbacks *allocation_callbacks = ignore_null_callback(&(inst->alloc_callbacks)); + if (cur_icd_term->instance) { + loader_icd_close_objects(inst, cur_icd_term); + cur_icd_term->dispatch.DestroyInstance(cur_icd_term->instance, allocation_callbacks); + } + cur_icd_term->instance = VK_NULL_HANDLE; + loader_icd_destroy(inst, cur_icd_term, allocation_callbacks); + cur_icd_term = NULL; + struct loader_scanned_icd *scanned_icd_to_remove = &inst->icd_tramp_list.scanned_list[cur_scanned_icd_index]; + // Iterate through preloaded ICDs and remove the corresponding driver from that list + loader_platform_thread_lock_mutex(&loader_preload_icd_lock); + if (NULL != preloaded_icds.scanned_list) { + for (uint32_t i = 0; i < preloaded_icds.count; i++) { + if (NULL != preloaded_icds.scanned_list[i].lib_name && NULL != scanned_icd_to_remove->lib_name && + strcmp(preloaded_icds.scanned_list[i].lib_name, scanned_icd_to_remove->lib_name) == 0) { + loader_unload_scanned_icd(NULL, &preloaded_icds.scanned_list[i]); + // condense the list so that it doesn't contain empty elements. + if (i < preloaded_icds.count - 1) { + memcpy((void *)&preloaded_icds.scanned_list[i], + (void *)&preloaded_icds.scanned_list[preloaded_icds.count - 1], + sizeof(struct loader_scanned_icd)); + memset((void *)&preloaded_icds.scanned_list[preloaded_icds.count - 1], 0, + sizeof(struct loader_scanned_icd)); + } + if (i > 0) { + preloaded_icds.count--; + } + + break; + } + } + } + loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); + + loader_unload_scanned_icd(inst, scanned_icd_to_remove); + } + + if (NULL == prev_icd_term) { + inst->icd_terms = next_icd_term; + } else { + prev_icd_term->next = next_icd_term; + } + } else { + prev_icd_term = cur_icd_term; + } + cur_icd_term = next_icd_term; + } +} VkResult setup_loader_tramp_phys_dev_groups(struct loader_instance *inst, uint32_t group_count, VkPhysicalDeviceGroupProperties *groups) { @@ -6757,19 +7097,21 @@ VkStringErrorFlags vk_string_validate(const int max_length, const char *utf8) { return result; } -VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceVersion(const VkEnumerateInstanceVersionChain *chain, - uint32_t *pApiVersion) { - (void)chain; +VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceVersion(uint32_t *pApiVersion) { // NOTE: The Vulkan WG doesn't want us checking pApiVersion for NULL, but instead // prefers us crashing. *pApiVersion = VK_HEADER_VERSION_COMPLETE; return VK_SUCCESS; } -VKAPI_ATTR VkResult VKAPI_CALL -terminator_EnumerateInstanceExtensionProperties(const VkEnumerateInstanceExtensionPropertiesChain *chain, const char *pLayerName, - uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { +VKAPI_ATTR VkResult VKAPI_CALL terminator_pre_instance_EnumerateInstanceVersion(const VkEnumerateInstanceVersionChain *chain, + uint32_t *pApiVersion) { (void)chain; + return terminator_EnumerateInstanceVersion(pApiVersion); +} + +VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pPropertyCount, + VkExtensionProperties *pProperties) { struct loader_extension_list *global_ext_list = NULL; struct loader_layer_list instance_layers; struct loader_extension_list local_ext_list; @@ -6821,7 +7163,7 @@ terminator_EnumerateInstanceExtensionProperties(const VkEnumerateInstanceExtensi if (VK_SUCCESS != res) { goto out; } - loader_scanned_icd_clear(NULL, &icd_tramp_list); + loader_clear_scanned_icd_list(NULL, &icd_tramp_list); // Append enabled implicit layers. res = loader_scan_for_implicit_layers(NULL, &instance_layers, &layer_filters); @@ -6864,18 +7206,21 @@ out: return res; } -VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceLayerProperties(const VkEnumerateInstanceLayerPropertiesChain *chain, - uint32_t *pPropertyCount, - VkLayerProperties *pProperties) { +VKAPI_ATTR VkResult VKAPI_CALL terminator_pre_instance_EnumerateInstanceExtensionProperties( + const VkEnumerateInstanceExtensionPropertiesChain *chain, const char *pLayerName, uint32_t *pPropertyCount, + VkExtensionProperties *pProperties) { (void)chain; + return terminator_EnumerateInstanceExtensionProperties(pLayerName, pPropertyCount, pProperties); +} + +VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceLayerProperties(uint32_t *pPropertyCount, + VkLayerProperties *pProperties) { VkResult result = VK_SUCCESS; struct loader_layer_list instance_layer_list; struct loader_envvar_all_filters layer_filters = {0}; LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize); - uint32_t copy_size; - result = parse_layer_environment_var_filters(NULL, &layer_filters); if (VK_SUCCESS != result) { goto out; @@ -6888,42 +7233,47 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceLayerProperties(const goto out; } - uint32_t active_layer_count = 0; + uint32_t layers_to_write_out = 0; for (uint32_t i = 0; i < instance_layer_list.count; i++) { if (instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON || instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_DEFAULT) { - active_layer_count++; + layers_to_write_out++; } } if (pProperties == NULL) { - *pPropertyCount = active_layer_count; + *pPropertyCount = layers_to_write_out; goto out; } - copy_size = (*pPropertyCount < active_layer_count) ? *pPropertyCount : active_layer_count; uint32_t output_properties_index = 0; - for (uint32_t i = 0; i < copy_size; i++) { - if (instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON || - instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_DEFAULT) { + for (uint32_t i = 0; i < instance_layer_list.count; i++) { + if (output_properties_index < *pPropertyCount && + (instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON || + instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_DEFAULT)) { memcpy(&pProperties[output_properties_index], &instance_layer_list.list[i].info, sizeof(VkLayerProperties)); output_properties_index++; } } - - *pPropertyCount = copy_size; - - if (copy_size < instance_layer_list.count) { + if (output_properties_index < layers_to_write_out) { + // Indicates that we had more elements to write but ran out of room result = VK_INCOMPLETE; - goto out; } + *pPropertyCount = output_properties_index; + out: loader_delete_layer_list_and_properties(NULL, &instance_layer_list); return result; } +VKAPI_ATTR VkResult VKAPI_CALL terminator_pre_instance_EnumerateInstanceLayerProperties( + const VkEnumerateInstanceLayerPropertiesChain *chain, uint32_t *pPropertyCount, VkLayerProperties *pProperties) { + (void)chain; + return terminator_EnumerateInstanceLayerProperties(pPropertyCount, pProperties); +} + // ---- Vulkan Core 1.1 terminators VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( @@ -6943,7 +7293,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( // For each ICD, query the number of physical device groups, and then get an // internal value for those physical devices. icd_term = inst->icd_terms; - for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) { + while (NULL != icd_term) { cur_icd_group_count = 0; // Get the function pointer to use to call into the ICD. This could be the core or KHR version @@ -6959,8 +7309,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of \'EnumeratePhysicalDevices\' " - "to ICD %d to get plain phys dev count.", - icd_idx); + "to ICD %s to get plain phys dev count.", + icd_term->scanned_icd->lib_name); continue; } } else { @@ -6969,12 +7319,13 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of " - "\'EnumeratePhysicalDeviceGroups\' to ICD %d to get count.", - icd_idx); + "\'EnumeratePhysicalDeviceGroups\' to ICD %s to get count.", + icd_term->scanned_icd->lib_name); continue; } } total_count += cur_icd_group_count; + icd_term = icd_term->next; } // If GPUs not sorted yet, look through them and generate list of all available GPUs @@ -7014,7 +7365,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( cur_icd_group_count = 0; icd_term = inst->icd_terms; - for (uint8_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) { + while (NULL != icd_term) { uint32_t count_this_time = total_count - cur_icd_group_count; // Get the function pointer to use to call into the ICD. This could be the core or KHR version @@ -7041,8 +7392,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of " - "\'EnumeratePhysicalDevices\' to ICD %d to get plain phys dev count.", - icd_idx); + "\'EnumeratePhysicalDevices\' to ICD %s to get plain phys dev count.", + icd_term->scanned_icd->lib_name); goto out; } @@ -7050,7 +7401,6 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( for (uint32_t indiv_gpu = 0; indiv_gpu < count_this_time; indiv_gpu++) { uint32_t cur_index = indiv_gpu + cur_icd_group_count; local_phys_dev_groups[cur_index].this_icd_term = icd_term; - local_phys_dev_groups[cur_index].icd_index = icd_idx; local_phys_dev_groups[cur_index].group_props.physicalDeviceCount = 1; local_phys_dev_groups[cur_index].group_props.physicalDevices[0] = phys_dev_array[indiv_gpu]; } @@ -7060,8 +7410,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of " - "\'EnumeratePhysicalDeviceGroups\' to ICD %d to get group count.", - icd_idx); + "\'EnumeratePhysicalDeviceGroups\' to ICD %s to get group count.", + icd_term->scanned_icd->lib_name); goto out; } if (cur_icd_group_count + count_this_time < *pPhysicalDeviceGroupCount) { @@ -7073,15 +7423,14 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of " - "\'EnumeratePhysicalDeviceGroups\' to ICD %d to get group information.", - icd_idx); + "\'EnumeratePhysicalDeviceGroups\' to ICD %s to get group information.", + icd_term->scanned_icd->lib_name); goto out; } for (uint32_t group = 0; group < count_this_time; ++group) { uint32_t cur_index = group + cur_icd_group_count; local_phys_dev_groups[cur_index].group_props = pPhysicalDeviceGroupProperties[cur_index]; local_phys_dev_groups[cur_index].this_icd_term = icd_term; - local_phys_dev_groups[cur_index].icd_index = icd_idx; } } else { // There's not enough space in the callee's allocated pPhysicalDeviceGroupProperties structs, @@ -7104,27 +7453,27 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of " - "\'EnumeratePhysicalDeviceGroups\' to ICD %d to get group information for temp data.", - icd_idx); + "\'EnumeratePhysicalDeviceGroups\' to ICD %s to get group information for temp data.", + icd_term->scanned_icd->lib_name); goto out; } for (uint32_t group = 0; group < count_this_time; ++group) { uint32_t cur_index = group + cur_icd_group_count; local_phys_dev_groups[cur_index].group_props = tmp_group_props[group]; local_phys_dev_groups[cur_index].this_icd_term = icd_term; - local_phys_dev_groups[cur_index].icd_index = icd_idx; } } if (VK_SUCCESS != res) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "terminator_EnumeratePhysicalDeviceGroups: Failed during dispatch call of " - "\'EnumeratePhysicalDeviceGroups\' to ICD %d to get content.", - icd_idx); + "\'EnumeratePhysicalDeviceGroups\' to ICD %s to get content.", + icd_term->scanned_icd->lib_name); goto out; } } cur_icd_group_count += count_this_time; + icd_term = icd_term->next; } #if defined(LOADER_ENABLE_LINUX_SORT) diff --git a/loader/loader.h b/loader/loader.h index 405d159af4efa4e87f92f5302511542b1d851598..7fded17e0f16734b1df1baa9cfd591c96d90fa36 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -95,7 +95,11 @@ VkResult loader_validate_instance_extensions(struct loader_instance *inst, const const struct loader_envvar_all_filters *layer_filters, const VkInstanceCreateInfo *pCreateInfo); +#if defined(_WIN32) +BOOL __stdcall loader_initialize(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context); +#else void loader_initialize(void); +#endif void loader_release(void); void loader_preload_icds(void); void loader_unload_preloaded_icds(void); @@ -120,6 +124,10 @@ VkResult copy_str_to_string_list(const struct loader_instance *inst, struct load void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list); VkResult loader_init_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info, size_t element_size); +VkResult loader_resize_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info); +VkResult loader_get_next_available_entry(const struct loader_instance *inst, struct loader_used_object_list *list_info, + uint32_t *free_index, const VkAllocationCallbacks *pAllocator); +void loader_release_object_from_list(struct loader_used_object_list *list_info, uint32_t index_to_free); bool has_vk_extension_property_array(const VkExtensionProperties *vk_ext_prop, const uint32_t count, const VkExtensionProperties *ext_array); bool has_vk_extension_property(const VkExtensionProperties *vk_ext_prop, const struct loader_extension_list *ext_list); @@ -147,13 +155,15 @@ VkResult loader_add_device_extensions(const struct loader_instance *inst, VkResult loader_init_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info, size_t element_size); void loader_destroy_generic_list(const struct loader_instance *inst, struct loader_generic_list *list); void loader_destroy_pointer_layer_list(const struct loader_instance *inst, struct loader_pointer_layer_list *layer_list); -void loader_delete_layer_list_and_properties(const struct loader_instance *inst, struct loader_layer_list *layer_list); +TEST_FUNCTION_EXPORT void loader_delete_layer_list_and_properties(const struct loader_instance *inst, + struct loader_layer_list *layer_list); void loader_remove_layer_in_list(const struct loader_instance *inst, struct loader_layer_list *layer_list, uint32_t layer_to_remove); -VkResult loader_scanned_icd_init(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list); -void loader_scanned_icd_clear(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list); +VkResult loader_init_scanned_icd_list(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list); +void loader_clear_scanned_icd_list(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list); VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list, const VkInstanceCreateInfo *pCreateInfo, bool *skipped_portability_drivers); +void loader_icd_close_objects(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term); void loader_icd_destroy(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term, const VkAllocationCallbacks *pAllocator); VkResult loader_scan_for_layers(struct loader_instance *inst, struct loader_layer_list *instance_layers, @@ -162,8 +172,9 @@ VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct lo const struct loader_envvar_all_filters *layer_filters); VkResult loader_get_icd_loader_instance_extensions(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list, struct loader_extension_list *inst_exts); -struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loader_device **found_dev, uint32_t *icd_index); +struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loader_device **found_dev); struct loader_instance *loader_get_instance(const VkInstance instance); +loader_platform_dl_handle loader_open_layer_file(const struct loader_instance *inst, struct loader_layer_properties *prop); struct loader_device *loader_create_logical_device(const struct loader_instance *inst, const VkAllocationCallbacks *pAllocator); void loader_add_logical_device(struct loader_icd_term *icd_term, struct loader_device *found_dev); void loader_remove_logical_device(struct loader_icd_term *icd_term, struct loader_device *found_dev, @@ -200,6 +211,7 @@ VkResult loader_validate_device_extensions(struct loader_instance *this_instance VkResult setup_loader_tramp_phys_devs(struct loader_instance *inst, uint32_t phys_dev_count, VkPhysicalDevice *phys_devs); VkResult setup_loader_tramp_phys_dev_groups(struct loader_instance *inst, uint32_t group_count, VkPhysicalDeviceGroupProperties *groups); +void unload_drivers_without_physical_devices(struct loader_instance *inst); VkStringErrorFlags vk_string_validate(const int max_length, const char *char_array); char *loader_get_next_path(char *path); diff --git a/loader/loader.rc b/loader/loader.rc index bf1280b1d9203cd456b180e172d139507f1806f1..e5fb1cb82d3b230df9bb7a3287a22294349b9fdb 100644 --- a/loader/loader.rc +++ b/loader/loader.rc @@ -1,7 +1,7 @@ // -// Copyright (c) 2014-2024 The Khronos Group Inc. -// Copyright (c) 2014-2024 Valve Corporation -// Copyright (c) 2014-2024 LunarG, Inc. +// Copyright (c) 2014-2025 The Khronos Group Inc. +// Copyright (c) 2014-2025 Valve Corporation +// Copyright (c) 2014-2025 LunarG, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,10 +22,10 @@ #include "winres.h" // All set through CMake -#define VER_FILE_VERSION 1, 3, 275, 0 -#define VER_FILE_DESCRIPTION_STR "1.3.275.Dev Build" +#define VER_FILE_VERSION 1, 4, 309, 0 +#define VER_FILE_DESCRIPTION_STR "1.4.309.Dev Build" #define VER_FILE_VERSION_STR "Vulkan Loader - Dev Build" -#define VER_COPYRIGHT_STR "Copyright (C) 2015-2024" +#define VER_COPYRIGHT_STR "Copyright (C) 2015-2025" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILE_VERSION diff --git a/loader/loader_common.h b/loader/loader_common.h index da6e28e3a96e942152f933341e288fcafc8262a5..e8df9d1138766b185e3f57a8c01a1a40f700173e 100644 --- a/loader/loader_common.h +++ b/loader/loader_common.h @@ -90,6 +90,40 @@ struct loader_device_extension_list { struct loader_dev_ext_props *list; }; +struct loader_used_object_status { + VkBool32 status; + VkAllocationCallbacks allocation_callbacks; +}; + +struct loader_used_object_list { + size_t capacity; + uint32_t padding; // count variable isn't used + struct loader_used_object_status *list; +}; + +struct loader_surface_allocation { + VkSurfaceKHR surface; + VkAllocationCallbacks allocation_callbacks; +}; + +struct loader_surface_list { + size_t capacity; + uint32_t padding; // count variable isn't used + VkSurfaceKHR *list; +}; + +struct loader_debug_utils_messenger_list { + size_t capacity; + uint32_t padding; // count variable isn't used + VkDebugUtilsMessengerEXT *list; +}; + +struct loader_debug_report_callback_list { + size_t capacity; + uint32_t padding; // count variable isn't used + VkDebugReportCallbackEXT *list; +}; + struct loader_name_value { char *name; char *value; @@ -129,6 +163,16 @@ enum layer_type_flags { VK_LAYER_TYPE_FLAG_META_LAYER = 0x4, // If not set, indicates standard layer }; +enum loader_layer_enabled_by_what { + ENABLED_BY_WHAT_UNSET, // default value indicates this field hasn't been filled in + ENABLED_BY_WHAT_LOADER_SETTINGS_FILE, + ENABLED_BY_WHAT_IMPLICIT_LAYER, + ENABLED_BY_WHAT_VK_INSTANCE_LAYERS, + ENABLED_BY_WHAT_VK_LOADER_LAYERS_ENABLE, + ENABLED_BY_WHAT_IN_APPLICATION_API, + ENABLED_BY_WHAT_META_LAYER, +}; + struct loader_layer_properties { VkLayerProperties info; enum layer_type_flags type_flags; @@ -138,6 +182,7 @@ struct loader_layer_properties { char *manifest_file_name; char *lib_name; enum loader_layer_library_status lib_status; + enum loader_layer_enabled_by_what enabled_by_what; loader_platform_dl_handle lib_handle; struct loader_layer_functions functions; struct loader_extension_list instance_extension_list; @@ -205,6 +250,9 @@ struct loader_device { bool ext_debug_marker_enabled; bool ext_debug_utils_enabled; bool ext_full_screen_exclusive_enabled; + bool version_1_1_enabled; + bool version_1_2_enabled; + bool version_1_3_enabled; } driver_extensions; struct loader_device *next; @@ -229,6 +277,13 @@ struct loader_icd_term { PFN_PhysDevExt phys_dev_ext[MAX_NUM_UNKNOWN_EXTS]; bool supports_get_dev_prop_2; + bool supports_ext_surface_maintenance_1; + + uint32_t physical_device_count; + + struct loader_surface_list surface_list; + struct loader_debug_utils_messenger_list debug_utils_messenger_list; + struct loader_debug_report_callback_list debug_report_callback_list; }; // Per ICD library structure @@ -276,7 +331,7 @@ struct loader_instance { struct loader_instance *next; - uint32_t total_icd_count; + uint32_t icd_terms_count; struct loader_icd_term *icd_terms; struct loader_icd_tramp_list icd_tramp_list; @@ -307,6 +362,11 @@ struct loader_instance { struct loader_extension_list ext_list; // icds and loaders extensions struct loader_instance_extension_enables enabled_known_extensions; + // Indicates which indices in the array are in-use and which are free to be reused + struct loader_used_object_list surfaces_list; + struct loader_used_object_list debug_utils_messengers_list; + struct loader_used_object_list debug_report_callbacks_list; + // Stores debug callbacks - used in the log. VkLayerDbgFunctionNode *current_dbg_function_head; // Current head VkLayerDbgFunctionNode *instance_only_dbg_function_head; // Only used for instance create/destroy @@ -400,7 +460,6 @@ struct loader_physical_device_tramp { struct loader_physical_device_term { struct loader_instance_dispatch_table *disp; // must be first entry in structure struct loader_icd_term *this_icd_term; - uint8_t icd_index; VkPhysicalDevice phys_dev; // object from ICD }; @@ -414,7 +473,6 @@ struct LinuxSortedDeviceInfo { bool default_device; // Loader specific items about the driver providing support for this physical device - uint32_t icd_index; struct loader_icd_term *icd_term; // Some generic device properties @@ -435,7 +493,6 @@ struct LinuxSortedDeviceInfo { // Per enumerated PhysicalDeviceGroup structure, used to wrap in terminator code struct loader_physical_device_group_term { struct loader_icd_term *this_icd_term; - uint8_t icd_index; VkPhysicalDeviceGroupProperties group_props; #if defined(LOADER_ENABLE_LINUX_SORT) struct LinuxSortedDeviceInfo internal_device_info[VK_MAX_DEVICE_GROUP_SIZE]; @@ -470,7 +527,6 @@ enum loader_data_files_type { struct loader_icd_physical_devices { uint32_t device_count; VkPhysicalDevice *physical_devices; - uint32_t icd_index; struct loader_icd_term *icd_term; #if defined(WIN32) LUID windows_adapter_luid; diff --git a/loader/loader_environment.c b/loader/loader_environment.c index 90aad168441e19838b082f0e1e8eca8ce26c5082..f813bb316298e25a92e1a9bfcf184c56eadb5a16 100644 --- a/loader/loader_environment.c +++ b/loader/loader_environment.c @@ -31,6 +31,7 @@ #include "allocation.h" #include "loader.h" #include "log.h" +#include "stack_allocation.h" #include #include "param/sys_param.h" @@ -38,7 +39,7 @@ // Environment variables #if COMMON_UNIX_PLATFORMS -bool is_high_integrity() { return geteuid() != getuid() || getegid() != getgid(); } +bool is_high_integrity(void) { return geteuid() != getuid() || getegid() != getgid(); } char *loader_getenv(const char *name, const struct loader_instance *inst) { if (NULL == name) return NULL; @@ -491,6 +492,7 @@ VkResult loader_add_environment_layers(struct loader_instance *inst, const enum // Only add it if it doesn't already appear in the layer list if (!loader_find_layer_name_in_list(source_prop->info.layerName, target_list)) { if (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { + source_prop->enabled_by_what = ENABLED_BY_WHAT_VK_INSTANCE_LAYERS; res = loader_add_layer_properties_to_list(inst, target_list, source_prop); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; res = loader_add_layer_properties_to_list(inst, expanded_target_list, source_prop); @@ -557,6 +559,7 @@ VkResult loader_add_environment_layers(struct loader_instance *inst, const enum // If not a meta-layer, simply add it. if (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { + source_prop->enabled_by_what = ENABLED_BY_WHAT_VK_LOADER_LAYERS_ENABLE; res = loader_add_layer_properties_to_list(inst, target_list, source_prop); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; res = loader_add_layer_properties_to_list(inst, expanded_target_list, source_prop); diff --git a/loader/loader_environment.h b/loader/loader_environment.h index 15474a3aba4b77d33a7a7a547eceb7fcd6ec4fc7..d120ec95c9da758983b52f425b12a1a538a2dcd5 100644 --- a/loader/loader_environment.h +++ b/loader/loader_environment.h @@ -37,7 +37,7 @@ void loader_free_getenv(char *val, const struct loader_instance *inst); #if defined(WIN32) || COMMON_UNIX_PLATFORMS -bool is_high_integrity(); +bool is_high_integrity(void); char *loader_secure_getenv(const char *name, const struct loader_instance *inst); diff --git a/loader/loader_json.c b/loader/loader_json.c new file mode 100644 index 0000000000000000000000000000000000000000..805d278c08184937fd86f2cf58ac0734ce284bed --- /dev/null +++ b/loader/loader_json.c @@ -0,0 +1,258 @@ +/* + Copyright (c) 2015-2021 The Khronos Group Inc. + Copyright (c) 2015-2021 Valve Corporation + Copyright (c) 2015-2021 LunarG, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "loader_json.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "cJSON.h" + +#include "allocation.h" +#include "loader.h" +#include "log.h" + +#if COMMON_UNIX_PLATFORMS +#include +#include +#endif + +#ifdef _WIN32 +static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, + char **out_buff) { + HANDLE file_handle = INVALID_HANDLE_VALUE; + DWORD len = 0, read_len = 0; + VkResult res = VK_SUCCESS; + BOOL read_ok = false; + + int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); + if (filename_utf16_size > 0) { + wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t)); + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) { + file_handle = + CreateFileW(filename_utf16, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + } + if (INVALID_HANDLE_VALUE == file_handle) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + len = GetFileSize(file_handle, NULL); + if (INVALID_FILE_SIZE == len) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + *out_buff = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == *out_buff) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + read_ok = ReadFile(file_handle, *out_buff, len, &read_len, NULL); + if (len != read_len || false == read_ok) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + *out_len = len + 1; + (*out_buff)[len] = '\0'; + +out: + if (INVALID_HANDLE_VALUE != file_handle) { + CloseHandle(file_handle); + } + return res; +} +#elif COMMON_UNIX_PLATFORMS +static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, + char **out_buff) { + FILE *file = NULL; + struct stat stats = {0}; + VkResult res = VK_SUCCESS; + + file = fopen(filename, "rb"); + if (NULL == file) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + if (-1 == fstat(fileno(file), &stats)) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + *out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == *out_buff) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + if (stats.st_size != (long int)fread(*out_buff, sizeof(char), stats.st_size, file)) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + *out_len = stats.st_size + 1; + (*out_buff)[stats.st_size] = '\0'; + +out: + if (NULL != file) { + fclose(file); + } + return res; +} +#else +#warning fopen not available on this platform +VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, char **out_buff) { + return VK_ERROR_INITIALIZATION_FAILED; +} +#endif + +TEST_FUNCTION_EXPORT VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) { + char *json_buf = NULL; + VkResult res = VK_SUCCESS; + + assert(json != NULL); + + size_t json_len = 0; + *json = NULL; + res = loader_read_entire_file(inst, filename, &json_len, &json_buf); + if (VK_SUCCESS != res) { + goto out; + } + bool out_of_memory = false; + // Parse text from file + *json = loader_cJSON_ParseWithLength(inst ? &inst->alloc_callbacks : NULL, json_buf, json_len, &out_of_memory); + if (out_of_memory) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.", + filename); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } else if (*json == NULL) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename); + goto out; + } + +out: + loader_instance_heap_free(inst, json_buf); + if (res != VK_SUCCESS && *json != NULL) { + loader_cJSON_Delete(*json); + *json = NULL; + } + + return res; +} + +VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key, size_t out_str_len, char *out_string) { + if (NULL == key) { + return VK_ERROR_INITIALIZATION_FAILED; + } + cJSON *item = loader_cJSON_GetObjectItem(object, key); + if (NULL == item) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + if (item->type != cJSON_String || item->valuestring == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + bool out_of_memory = false; + bool success = loader_cJSON_PrintPreallocated(item, out_string, (int)out_str_len, cJSON_False); + if (out_of_memory) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (!success) { + return VK_ERROR_INITIALIZATION_FAILED; + } + return VK_SUCCESS; +} + +VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) { + if (NULL == key) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + cJSON *item = loader_cJSON_GetObjectItem(object, key); + if (NULL == item || NULL == item->valuestring) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + bool out_of_memory = false; + char *str = loader_cJSON_Print(item, &out_of_memory); + if (out_of_memory || NULL == str) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (NULL != out_string) { + *out_string = str; + } + return VK_SUCCESS; +} +VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, + struct loader_string_list *string_list) { + if (NULL == key) { + return VK_ERROR_INITIALIZATION_FAILED; + } + cJSON *item = loader_cJSON_GetObjectItem(object, key); + if (NULL == item) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + uint32_t count = loader_cJSON_GetArraySize(item); + if (count == 0) { + return VK_SUCCESS; + } + + VkResult res = create_string_list(inst, count, string_list); + if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { + goto out; + } + cJSON *element = NULL; + cJSON_ArrayForEach(element, item) { + if (element->type != cJSON_String) { + return VK_ERROR_INITIALIZATION_FAILED; + } + bool out_of_memory = false; + char *out_data = loader_cJSON_Print(element, &out_of_memory); + if (out_of_memory) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + res = append_str_to_string_list(inst, string_list, out_data); + if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { + goto out; + } + } +out: + if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) { + free_string_list(inst, string_list); + } + + return res; +} diff --git a/loader/loader_json.h b/loader/loader_json.h new file mode 100644 index 0000000000000000000000000000000000000000..b78ffc95daa27f0609650c4cbba436786025cba3 --- /dev/null +++ b/loader/loader_json.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2015-2021 The Khronos Group Inc. + Copyright (c) 2015-2021 Valve Corporation + Copyright (c) 2015-2021 LunarG, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#pragma once + +#include + +#include + +#include + +// Forward decls +struct loader_instance; +struct loader_string_list; + +// Read a JSON file into a buffer. +// +// @return - A pointer to a cJSON object representing the JSON parse tree. +// This returned buffer should be freed by caller. +TEST_FUNCTION_EXPORT VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json); + +// Given a cJSON object, find the string associated with the key and puts an pre-allocated string into out_string. +// Length is given by out_str_len, and this function truncates the string with a null terminator if it the provided space isn't +// large enough. +VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key, size_t out_str_len, char *out_string); + +// Given a cJSON object, find the string associated with the key and puts an allocated string into out_string. +// It is the callers responsibility to free out_string. +VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string); + +// Given a cJSON object, find the array of strings associated with they key and writes the count into out_count and data into +// out_array_of_strings. It is the callers responsibility to free out_array_of_strings. +VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key, + struct loader_string_list *string_list); diff --git a/loader/loader_linux.c b/loader/loader_linux.c index e0b3db81b20126b53e0d0b7fb279a84429b591e5..c556010552bb3177490726975a193ac725704217 100644 --- a/loader/loader_linux.c +++ b/loader/loader_linux.c @@ -33,6 +33,7 @@ #include "loader_environment.h" #include "loader.h" #include "log.h" +#include "stack_allocation.h" // Determine a priority based on device type with the higher value being higher priority. uint32_t determine_priority_type_value(VkPhysicalDeviceType type) { @@ -256,7 +257,6 @@ VkResult linux_read_sorted_physical_devices(struct loader_instance *inst, uint32 VkPhysicalDeviceProperties dev_props = {}; sorted_device_info[index].physical_device = icd_devices[icd_idx].physical_devices[phys_dev]; - sorted_device_info[index].icd_index = icd_idx; sorted_device_info[index].icd_term = icd_term; sorted_device_info[index].has_pci_bus_info = false; @@ -293,8 +293,8 @@ VkResult linux_read_sorted_physical_devices(struct loader_instance *inst, uint32 if (sorted_device_info[index].has_pci_bus_info) { VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = (VkPhysicalDevicePCIBusInfoPropertiesEXT){ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT}; - VkPhysicalDeviceProperties2 dev_props2 = (VkPhysicalDeviceProperties2){ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = (VkBaseInStructure *)&pci_props}; + VkPhysicalDeviceProperties2 dev_props2 = + (VkPhysicalDeviceProperties2){.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &pci_props}; PFN_vkGetPhysicalDeviceProperties2 GetPhysDevProps2 = NULL; if (app_is_vulkan_1_1 && device_is_1_1_capable) { @@ -330,7 +330,6 @@ VkResult linux_read_sorted_physical_devices(struct loader_instance *inst, uint32 // Add all others after (they've already been sorted) for (uint32_t dev = 0; dev < phys_dev_count; ++dev) { sorted_device_term[dev]->this_icd_term = sorted_device_info[dev].icd_term; - sorted_device_term[dev]->icd_index = sorted_device_info[dev].icd_index; sorted_device_term[dev]->phys_dev = sorted_device_info[dev].physical_device; loader_set_dispatch((void *)sorted_device_term[dev], inst->disp); loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, " [%u] %s %s", dev, @@ -396,8 +395,8 @@ VkResult linux_sort_physical_device_groups(struct loader_instance *inst, uint32_ if (sorted_group_term[group].internal_device_info[gpu].has_pci_bus_info) { VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = (VkPhysicalDevicePCIBusInfoPropertiesEXT){ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT}; - VkPhysicalDeviceProperties2 dev_props2 = (VkPhysicalDeviceProperties2){ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = (VkBaseInStructure *)&pci_props}; + VkPhysicalDeviceProperties2 dev_props2 = + (VkPhysicalDeviceProperties2){.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &pci_props}; PFN_vkGetPhysicalDeviceProperties2 GetPhysDevProps2 = NULL; if (app_is_vulkan_1_1 && device_is_1_1_capable) { diff --git a/loader/loader_windows.c b/loader/loader_windows.c index 5b0dfb0dab70fae51f5f87d1ccfaf2d8e5eb5ea4..f73659c09122320d256154d03ab7fb9e60d397bd 100644 --- a/loader/loader_windows.c +++ b/loader/loader_windows.c @@ -79,7 +79,7 @@ void windows_initialization(void) { // This is needed to ensure that newer APIs are available right away // and not after the first call that has been statically linked - LoadLibrary("gdi32.dll"); + LoadLibraryEx("gdi32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); wchar_t systemPath[MAX_PATH] = L""; GetSystemDirectoryW(systemPath, MAX_PATH); @@ -99,7 +99,11 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)hinst; switch (reason) { case DLL_PROCESS_ATTACH: - loader_initialize(); + // Only initialize necessary sync primitives + loader_platform_thread_create_mutex(&loader_lock); + loader_platform_thread_create_mutex(&loader_preload_icd_lock); + loader_platform_thread_create_mutex(&loader_global_instance_list_lock); + init_global_loader_settings(); break; case DLL_PROCESS_DETACH: if (NULL == reserved) { @@ -141,7 +145,7 @@ bool windows_add_json_entry(const struct loader_instance *inst, loader_instance_heap_realloc(inst, *reg_data, *total_size, *total_size * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (NULL == new_ptr) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "windows_add_json_entry: Failed to reallocate space for registry value of size %d for key %s", + "windows_add_json_entry: Failed to reallocate space for registry value of size %ld for key %s", *total_size * 2, json_path); *result = VK_ERROR_OUT_OF_HOST_MEMORY; return false; @@ -179,7 +183,7 @@ bool windows_get_device_registry_entry(const struct loader_instance *inst, char CONFIGRET status = CM_Open_DevNode_Key(dev_id, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hkrKey, CM_REGISTRY_SOFTWARE); if (status != CR_SUCCESS) { loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: Failed to open registry key for DeviceID(%d)", dev_id); + "windows_get_device_registry_entry: Failed to open registry key for DeviceID(%ld)", dev_id); *result = VK_ERROR_INCOMPATIBLE_DRIVER; return false; } @@ -190,10 +194,10 @@ bool windows_get_device_registry_entry(const struct loader_instance *inst, char if (ret != ERROR_SUCCESS) { if (ret == ERROR_FILE_NOT_FOUND) { loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: Device ID(%d) Does not contain a value for \"%s\"", dev_id, value_name); + "windows_get_device_registry_entry: Device ID(%ld) Does not contain a value for \"%s\"", dev_id, value_name); } else { loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s size", dev_id, value_name); + "windows_get_device_registry_entry: DeviceID(%ld) Failed to obtain %s size", dev_id, value_name); } goto out; } @@ -210,7 +214,7 @@ bool windows_get_device_registry_entry(const struct loader_instance *inst, char if (ret != ERROR_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, - "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s", value_name); + "windows_get_device_registry_entry: DeviceID(%ld) Failed to obtain %s", dev_id, value_name); *result = VK_ERROR_INCOMPATIBLE_DRIVER; goto out; } @@ -304,7 +308,7 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u status = CM_Get_Child(&childID, devID, 0); if (status != CR_SUCCESS) { loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0, - "windows_get_device_registry_files: unable to open child-device error:%d", status); + "windows_get_device_registry_files: unable to open child-device error:%ld", status); continue; } @@ -313,12 +317,12 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u CM_Get_Device_IDW(childID, buffer, MAX_DEVICE_ID_LEN, 0); loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0, - "windows_get_device_registry_files: Opening child device %d - %ls", childID, buffer); + "windows_get_device_registry_files: Opening child device %ld - %ls", childID, buffer); status = CM_Get_DevNode_Registry_PropertyW(childID, CM_DRP_CLASSGUID, NULL, &childGuid, &childGuidSize, 0); if (status != CR_SUCCESS) { loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0, - "windows_get_device_registry_files: unable to obtain GUID for:%d error:%d", childID, status); + "windows_get_device_registry_files: unable to obtain GUID for:%ld error:%ld", childID, status); result = VK_ERROR_INCOMPATIBLE_DRIVER; continue; @@ -326,7 +330,7 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u if (wcscmp(childGuid, softwareComponentGUID) != 0) { loader_log(inst, VULKAN_LOADER_DEBUG_BIT | log_target_flag, 0, - "windows_get_device_registry_files: GUID for %d is not SoftwareComponent skipping", childID); + "windows_get_device_registry_files: GUID for %ld is not SoftwareComponent skipping", childID); continue; } @@ -447,7 +451,7 @@ VkResult windows_get_registry_files(const struct loader_instance *inst, char *lo if (NULL == new_ptr) { loader_log( inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0, - "windows_get_registry_files: Failed to reallocate space for registry value of size %d for key %s", + "windows_get_registry_files: Failed to reallocate space for registry value of size %ld for key %s", *reg_data_size * 2, name); RegCloseKey(key); result = VK_ERROR_OUT_OF_HOST_MEMORY; @@ -793,21 +797,19 @@ out: return vk_result; } -VkResult enumerate_adapter_physical_devices(struct loader_instance *inst, struct loader_icd_term *icd_term, uint32_t icd_idx, - LUID luid, uint32_t *icd_phys_devs_array_count, +VkResult enumerate_adapter_physical_devices(struct loader_instance *inst, struct loader_icd_term *icd_term, LUID luid, + uint32_t *icd_phys_devs_array_count, struct loader_icd_physical_devices *icd_phys_devs_array) { uint32_t count = 0; VkResult res = icd_term->scanned_icd->EnumerateAdapterPhysicalDevices(icd_term->instance, luid, &count, NULL); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { return res; - } else if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { + } else if (res == VK_ERROR_INCOMPATIBLE_DRIVER || res == VK_ERROR_INITIALIZATION_FAILED || 0 == count) { return VK_SUCCESS; // This driver doesn't support the adapter } else if (res != VK_SUCCESS) { loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, - "Failed to convert DXGI adapter into Vulkan physical device with unexpected error code"); - return res; - } else if (0 == count) { - return VK_SUCCESS; // This driver doesn't support the adapter + "Failed to convert DXGI adapter into Vulkan physical device with unexpected error code: %d", res); + return VK_SUCCESS; } // Take a pointer to the last element of icd_phys_devs_array to simplify usage @@ -856,10 +858,14 @@ VkResult enumerate_adapter_physical_devices(struct loader_instance *inst, struct } if (!already_enumerated) { next_icd_phys_devs->device_count = count; - next_icd_phys_devs->icd_index = icd_idx; next_icd_phys_devs->icd_term = icd_term; next_icd_phys_devs->windows_adapter_luid = luid; (*icd_phys_devs_array_count)++; + } else { + // Avoid memory leak in case of the already_enumerated hitting true + // at the last enumerate_adapter_physical_devices call in the outer loop + loader_instance_heap_free(inst, next_icd_phys_devs->physical_devices); + next_icd_phys_devs->physical_devices = NULL; } return VK_SUCCESS; @@ -962,33 +968,35 @@ VkResult windows_read_sorted_physical_devices(struct loader_instance *inst, uint continue; } - if (icd_phys_devs_array_size <= i) { - uint32_t old_size = icd_phys_devs_array_size * sizeof(struct loader_icd_physical_devices); - *icd_phys_devs_array = loader_instance_heap_realloc(inst, *icd_phys_devs_array, old_size, 2 * old_size, - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (*icd_phys_devs_array == NULL) { - adapter->lpVtbl->Release(adapter); - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; - } - icd_phys_devs_array_size *= 2; - } - (*icd_phys_devs_array)[*icd_phys_devs_array_count].device_count = 0; - (*icd_phys_devs_array)[*icd_phys_devs_array_count].physical_devices = NULL; - icd_term = inst->icd_terms; - for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) { + while (NULL != icd_term) { // This is the new behavior, which cannot be run unless the ICD provides EnumerateAdapterPhysicalDevices if (icd_term->scanned_icd->EnumerateAdapterPhysicalDevices == NULL) { + icd_term = icd_term->next; continue; } - res = enumerate_adapter_physical_devices(inst, icd_term, icd_idx, description.AdapterLuid, icd_phys_devs_array_count, + if (icd_phys_devs_array_size <= *icd_phys_devs_array_count) { + uint32_t old_size = icd_phys_devs_array_size * sizeof(struct loader_icd_physical_devices); + *icd_phys_devs_array = loader_instance_heap_realloc(inst, *icd_phys_devs_array, old_size, 2 * old_size, + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (*icd_phys_devs_array == NULL) { + adapter->lpVtbl->Release(adapter); + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + icd_phys_devs_array_size *= 2; + } + (*icd_phys_devs_array)[*icd_phys_devs_array_count].device_count = 0; + (*icd_phys_devs_array)[*icd_phys_devs_array_count].physical_devices = NULL; + + res = enumerate_adapter_physical_devices(inst, icd_term, description.AdapterLuid, icd_phys_devs_array_count, *icd_phys_devs_array); if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { adapter->lpVtbl->Release(adapter); goto out; } + icd_term = icd_term->next; } adapter->lpVtbl->Release(adapter); @@ -1103,10 +1111,8 @@ char *windows_get_app_package_manifest_path(const struct loader_instance *inst) } UINT32 numPackages = 0, bufferLength = 0; - /* This literal string identifies the Microsoft-published OpenCL and OpenGL Compatibility Pack - * (so named at the time this is being added), which contains OpenGLOn12 and OpenCLOn12 mapping - * layers, and will contain VulkanOn12 (aka Dozen) going forward. - */ + // This literal string identifies the Microsoft-published OpenCL, OpenGL, and Vulkan Compatibility Pack, which contains + // OpenGLOn12, OpenCLOn12, and VulkanOn12 (aka Dozen) mappinglayers PCWSTR familyName = L"Microsoft.D3DMappingLayers_8wekyb3d8bbwe"; if (ERROR_INSUFFICIENT_BUFFER != fpGetPackagesByPackageFamily(familyName, &numPackages, NULL, &bufferLength, NULL) || numPackages == 0 || bufferLength == 0) { @@ -1188,12 +1194,15 @@ VkResult get_settings_path_if_exists_in_registry_key(const struct loader_instanc } } - // Make sure the path exists first - if (*out_path && !loader_platform_file_exists(name)) { - return VK_ERROR_INITIALIZATION_FAILED; - } - if (strcmp(VK_LOADER_SETTINGS_FILENAME, &(name[start_of_path_filename])) == 0) { + // Make sure the path exists first + if (!loader_platform_file_exists(name)) { + loader_log( + inst, VULKAN_LOADER_DEBUG_BIT, 0, + "Registry contained entry to vk_loader_settings.json but the corresponding file does not exist, ignoring"); + return VK_ERROR_INITIALIZATION_FAILED; + } + *out_path = loader_instance_heap_calloc(inst, name_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (*out_path == NULL) { return VK_ERROR_OUT_OF_HOST_MEMORY; @@ -1211,41 +1220,37 @@ VkResult get_settings_path_if_exists_in_registry_key(const struct loader_instanc VkResult windows_get_loader_settings_file_path(const struct loader_instance *inst, char **out_path) { VkResult result = VK_SUCCESS; DWORD access_flags = KEY_QUERY_VALUE; + LONG rtn_value = 0; HKEY key = NULL; - *out_path = NULL; - // if we are running with admin privileges, only check HKEY_LOCAL_MACHINE. - // Otherwise check HKEY_CURRENT_USER, and if nothing is there, look in HKEY_LOCAL_MACHINE + // Search in HKEY_CURRENT_USER first if we are running without admin privileges + // Exit if a settings file was found. + // Otherwise check in HKEY_LOCAL_MACHINE. - if (is_high_integrity()) { - LONG rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key); - if (ERROR_SUCCESS != rtn_value) { - result = VK_ERROR_FEATURE_NOT_PRESENT; - goto out; - } - result = get_settings_path_if_exists_in_registry_key(inst, out_path, key); - } else { - LONG rtn_value = RegOpenKeyEx(HKEY_CURRENT_USER, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key); + if (!is_high_integrity()) { + rtn_value = RegOpenKeyEx(HKEY_CURRENT_USER, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key); if (ERROR_SUCCESS == rtn_value) { result = get_settings_path_if_exists_in_registry_key(inst, out_path, key); - RegCloseKey(key); + // Either we got OOM and *must* exit or we successfully found the settings file and can exit if (result == VK_ERROR_OUT_OF_HOST_MEMORY || result == VK_SUCCESS) { goto out; } + RegCloseKey(key); + key = NULL; } + } - rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key); - if (ERROR_SUCCESS != rtn_value) { - result = VK_ERROR_FEATURE_NOT_PRESENT; - goto out; - } + rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key); + if (ERROR_SUCCESS != rtn_value) { + result = VK_ERROR_FEATURE_NOT_PRESENT; + goto out; + } - result = get_settings_path_if_exists_in_registry_key(inst, out_path, key); - if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { - goto out; - } + result = get_settings_path_if_exists_in_registry_key(inst, out_path, key); + if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { + goto out; } out: diff --git a/loader/log.c b/loader/log.c index e92978768e0e88fbbfac3ad20a86adc217bb0803..613476bb680146a1c388f8891120ca15f8ccfb9f 100644 --- a/loader/log.c +++ b/loader/log.c @@ -91,7 +91,54 @@ void loader_init_global_debug_level(void) { void loader_set_global_debug_level(uint32_t new_loader_debug) { g_loader_debug = new_loader_debug; } -void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) { +void generate_debug_flag_str(VkFlags msg_type, size_t cmd_line_size, char *cmd_line_msg) { + cmd_line_msg[0] = '\0'; + + if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { + loader_strncat(cmd_line_msg, cmd_line_size, "ERROR", sizeof("ERROR")); + } + if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { + if (strlen(cmd_line_msg) > 0) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); + } + loader_strncat(cmd_line_msg, cmd_line_size, "WARNING", sizeof("WARNING")); + } + if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { + if (strlen(cmd_line_msg) > 0) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); + } + loader_strncat(cmd_line_msg, cmd_line_size, "INFO", sizeof("INFO")); + } + if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { + if (strlen(cmd_line_msg) > 0) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); + } + loader_strncat(cmd_line_msg, cmd_line_size, "DEBUG", sizeof("DEBUG")); + } + if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { + if (strlen(cmd_line_msg) > 0) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); + } + loader_strncat(cmd_line_msg, cmd_line_size, "PERF", sizeof("PERF")); + } + if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { + if (strlen(cmd_line_msg) > 0) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); + } + loader_strncat(cmd_line_msg, cmd_line_size, "DRIVER", sizeof("DRIVER")); + } + if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) { + if (strlen(cmd_line_msg) > 0) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); + } + loader_strncat(cmd_line_msg, cmd_line_size, "LAYER", sizeof("LAYER")); + } + +#undef STRNCAT_TO_BUFFER +} + +void DECORATE_PRINTF(4, 5) + loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) { (void)msg_code; char msg[512] = {0}; @@ -146,70 +193,72 @@ void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t ms // Always log to stderr if this is a fatal error if (0 == (msg_type & VULKAN_LOADER_FATAL_ERROR_BIT)) { - // Exit early if the current instance settings do not ask for logging to stderr - if (inst && inst->settings.settings_active && 0 == (msg_type & inst->settings.debug_level)) { - return; + if (inst && inst->settings.settings_active && inst->settings.debug_level > 0) { + // Exit early if the current instance settings have some debugging options but do match the current msg_type + if (0 == (msg_type & inst->settings.debug_level)) { + return; + } // Check the global settings and if that doesn't say to skip, check the environment variable } else if (0 == (msg_type & g_loader_debug)) { return; } } +#if defined(DEBUG) + int debug_flag_mask = + msg_type & (VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DEBUG_BIT); + assert((debug_flag_mask == 0 || debug_flag_mask == VULKAN_LOADER_ERROR_BIT || debug_flag_mask == VULKAN_LOADER_WARN_BIT || + debug_flag_mask == VULKAN_LOADER_INFO_BIT || debug_flag_mask == VULKAN_LOADER_DEBUG_BIT) && + "This log has more than one exclusive debug flags (error, warn, info, debug) set"); +#endif + // Only need enough space to create the filter description header for log messages // Also use the same header for all output - char cmd_line_msg[64]; + char cmd_line_msg[64] = {0}; size_t cmd_line_size = sizeof(cmd_line_msg); - size_t num_used = 0; - - cmd_line_msg[0] = '\0'; -// Helper macro which strncat's the given string literal, then updates num_used & cmd_line_end -// Assumes that we haven't used the entire buffer - must manually check this when adding new filter types -// We concat at the end of cmd_line_msg, so that strncat isn't a victim of Schlemiel the Painter -// We write to the end - 1 of cmd_line_msg, as the end is actually a null terminator -#define STRNCAT_TO_BUFFER(string_literal_to_cat) \ - loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, string_literal_to_cat, sizeof(string_literal_to_cat)); \ - num_used += sizeof(string_literal_to_cat) - 1; // subtract one to remove the null terminator in the string literal + loader_strncat(cmd_line_msg, cmd_line_size, "[Vulkan Loader] ", sizeof("[Vulkan Loader] ")); + bool need_separator = false; if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { - STRNCAT_TO_BUFFER("ERROR"); + loader_strncat(cmd_line_msg, cmd_line_size, "ERROR", sizeof("ERROR")); + need_separator = true; } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { - STRNCAT_TO_BUFFER("WARNING"); + loader_strncat(cmd_line_msg, cmd_line_size, "WARNING", sizeof("WARNING")); + need_separator = true; } else if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { - STRNCAT_TO_BUFFER("INFO"); + loader_strncat(cmd_line_msg, cmd_line_size, "INFO", sizeof("INFO")); + need_separator = true; } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { - STRNCAT_TO_BUFFER("DEBUG"); + loader_strncat(cmd_line_msg, cmd_line_size, "DEBUG", sizeof("DEBUG")); + need_separator = true; } if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { - if (num_used > 1) { - STRNCAT_TO_BUFFER(" | "); + if (need_separator) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); } - STRNCAT_TO_BUFFER("PERF"); - } - if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { - if (num_used > 1) { - STRNCAT_TO_BUFFER(" | "); + loader_strncat(cmd_line_msg, cmd_line_size, "PERF", sizeof("PERF")); + } else if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { + if (need_separator) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); } - STRNCAT_TO_BUFFER("DRIVER"); - } - if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) { - if (num_used > 1) { - STRNCAT_TO_BUFFER(" | "); + loader_strncat(cmd_line_msg, cmd_line_size, "DRIVER", sizeof("DRIVER")); + } else if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) { + if (need_separator) { + loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); } - STRNCAT_TO_BUFFER("LAYER"); + loader_strncat(cmd_line_msg, cmd_line_size, "LAYER", sizeof("LAYER")); } - // Add a ": " to separate the filters from the message - STRNCAT_TO_BUFFER(": "); -#undef STRNCAT_TO_BUFFER + loader_strncat(cmd_line_msg, cmd_line_size, ": ", sizeof(": ")); + size_t num_used = strlen(cmd_line_msg); - // Justifies the output to at least 19 spaces - if (num_used < 19) { - const char *space_buffer = " "; - // Only write (19 - num_used) spaces - loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, space_buffer, 19 - num_used); - num_used += sizeof(space_buffer) - 1 - num_used; + // Justifies the output to at least 29 spaces + if (num_used < 32) { + const char space_buffer[] = " "; + // Only write (32 - num_used) spaces + loader_strncat(cmd_line_msg, cmd_line_size, space_buffer, sizeof(space_buffer) - 1 - num_used); } // Assert that we didn't write more than what is available in cmd_line_msg assert(cmd_line_size > num_used); diff --git a/loader/log.h b/loader/log.h index c64cd5afd2cd1a56a9883311079069ac10303dea..1b594ab7795dc9ec1a455cf74aa62b208b9789a0 100644 --- a/loader/log.h +++ b/loader/log.h @@ -54,6 +54,9 @@ void loader_init_global_debug_level(void); // Sets the global debug level - used by global settings files void loader_set_global_debug_level(uint32_t new_loader_debug); +// Writes a stringified version of enum vulkan_loader_debug_flags into a char array cmd_line_msg of length cmd_line_size +void generate_debug_flag_str(VkFlags msg_type, size_t cmd_line_size, char *cmd_line_msg); + // The asm declaration prevents name mangling which is necessary for macOS #if defined(MODIFY_UNKNOWN_FUNCTION_DECLS) #define ASM_NAME(name) __asm(name) @@ -61,10 +64,18 @@ void loader_set_global_debug_level(uint32_t new_loader_debug); #define ASM_NAME(name) #endif +#if defined(__clang__) +#define DECORATE_PRINTF(_fmt_argnum, _first_param_num) __attribute__((format(printf, _fmt_argnum, _first_param_num))) +#elif defined(__GNUC__) +#define DECORATE_PRINTF(_fmt_argnum, _first_param_num) __attribute__((format(gnu_printf, _fmt_argnum, _first_param_num))) +#else +#define DECORATE_PRINTF(_fmt_num, _first_param_num) +#endif + // Logs a message to stderr // May output to DebugUtils if the instance isn't null and the extension is enabled. -void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) - ASM_NAME("loader_log"); +void DECORATE_PRINTF(4, 5) loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, + ...) ASM_NAME("loader_log"); // Used for the assembly code to emit an specific error message // This is a work around for linux 32 bit error handling not passing relocatable strings correctly diff --git a/loader/phys_dev_ext.c b/loader/phys_dev_ext.c index 1bcfa5767b56ae7011ad2bcb4c1c8f8eb3dde775..1ea897ea1af06603c1a80d5adf75df73e5d60404 100644 --- a/loader/phys_dev_ext.c +++ b/loader/phys_dev_ext.c @@ -28,7 +28,7 @@ // unknown to the loader, it will use this code. Technically, this is not trampoline // code since we don't want to optimize it out. -#include "loader.h" +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC optimize(3) // force gcc to use tail-calls diff --git a/loader/settings.c b/loader/settings.c index 4e037bce71e85883a6f55407e349718d047c3639..fce896e832e565f47925ffaca2fc8c238387279c 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -27,7 +27,10 @@ #include "cJSON.h" #include "loader.h" #include "loader_environment.h" +#include "loader_json.h" +#if defined(WIN32) #include "loader_windows.h" +#endif #include "log.h" #include "stack_allocation.h" #include "vk_loader_platform.h" @@ -106,17 +109,15 @@ uint32_t parse_log_filters_from_strings(struct loader_string_list* log_filters) return filters; } -bool parse_json_enable_disable_option(const struct loader_instance* inst, cJSON* object, const char* key) { - char* str = NULL; - VkResult res = loader_parse_json_string(object, key, &str); - if (res != VK_SUCCESS || NULL == str) { +bool parse_json_enable_disable_option(cJSON* object) { + char* str = loader_cJSON_GetStringValue(object); + if (NULL == str) { return false; } bool enable = false; if (strcmp(str, "enabled") == 0) { enable = true; } - loader_instance_heap_free(inst, str); return enable; } @@ -160,8 +161,9 @@ VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* s VkResult res = VK_SUCCESS; cJSON* layer_configurations = loader_cJSON_GetObjectItem(settings_object, "layers"); + // If the layers object isn't present, return early with success to allow the settings file to still apply if (NULL == layer_configurations) { - return VK_ERROR_INITIALIZATION_FAILED; + return VK_SUCCESS; } uint32_t layer_configurations_count = loader_cJSON_GetArraySize(layer_configurations); @@ -178,13 +180,14 @@ VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* s goto out; } - for (uint32_t i = 0; i < layer_configurations_count; i++) { - cJSON* layer = loader_cJSON_GetArrayItem(layer_configurations, i); - if (NULL == layer) { + cJSON* layer = NULL; + size_t i = 0; + cJSON_ArrayForEach(layer, layer_configurations) { + if (layer->type != cJSON_Object) { res = VK_ERROR_INITIALIZATION_FAILED; goto out; } - res = parse_layer_configuration(inst, layer, &(loader_settings->layer_configurations[i])); + res = parse_layer_configuration(inst, layer, &(loader_settings->layer_configurations[i++])); if (VK_SUCCESS != res) { goto out; } @@ -192,8 +195,8 @@ VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* s out: if (res != VK_SUCCESS) { if (loader_settings->layer_configurations) { - for (uint32_t i = 0; i < loader_settings->layer_configuration_count; i++) { - free_layer_configuration(inst, &(loader_settings->layer_configurations[i])); + for (size_t index = 0; index < loader_settings->layer_configuration_count; index++) { + free_layer_configuration(inst, &(loader_settings->layer_configurations[index])); } loader_settings->layer_configuration_count = 0; loader_instance_heap_free(inst, loader_settings->layer_configurations); @@ -204,7 +207,8 @@ out: return res; } -VkResult check_if_settings_path_exists(const struct loader_instance* inst, char* base, char* suffix, char** settings_file_path) { +VkResult check_if_settings_path_exists(const struct loader_instance* inst, const char* base, const char* suffix, + char** settings_file_path) { if (NULL == base || NULL == suffix) { return VK_ERROR_INITIALIZATION_FAILED; } @@ -276,12 +280,24 @@ void log_settings(const struct loader_instance* inst, loader_settings* settings) loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Using layer configurations found in loader settings from %s", settings->settings_file_path); + char cmd_line_msg[64] = {0}; + size_t cmd_line_size = sizeof(cmd_line_msg); + + cmd_line_msg[0] = '\0'; + + generate_debug_flag_str(settings->debug_level, cmd_line_size, cmd_line_msg); + if (strlen(cmd_line_msg)) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Loader Settings Filters for Logging to Standard Error: %s", cmd_line_msg); + } + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Layer Configurations count = %d", settings->layer_configuration_count); for (uint32_t i = 0; i < settings->layer_configuration_count; i++) { loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---- Layer Configuration [%d] ----", i); if (settings->layer_configurations[i].control != LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) { loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Name: %s", settings->layer_configurations[i].name); loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Path: %s", settings->layer_configurations[i].path); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Layer Type: %s", + settings->layer_configurations[i].treat_as_implicit_manifest ? "Implicit" : "Explicit"); } loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Control: %s", loader_settings_layer_control_to_string(settings->layer_configurations[i].control)); @@ -300,12 +316,16 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings #if defined(WIN32) res = windows_get_loader_settings_file_path(inst, &settings_file_path); if (res != VK_SUCCESS) { + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, + "No valid vk_loader_settings.json file found, no loader settings will be active"); goto out; } #elif COMMON_UNIX_PLATFORMS res = get_unix_settings_path(inst, &settings_file_path); if (res != VK_SUCCESS) { + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, + "No valid vk_loader_settings.json file found, no loader settings will be active"); goto out; } #else @@ -314,93 +334,101 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings res = loader_get_json(inst, settings_file_path, &json); // Make sure sure the top level json value is an object - if (res != VK_SUCCESS || NULL == json || json->type != 6) { + if (res != VK_SUCCESS || NULL == json || json->type != cJSON_Object) { goto out; } res = loader_parse_json_string(json, "file_format_version", &file_format_version_string); if (res != VK_SUCCESS) { + if (res != VK_ERROR_OUT_OF_HOST_MEMORY) { + loader_log( + inst, VULKAN_LOADER_DEBUG_BIT, 0, + "Loader settings file from %s missing required field file_format_version - no loader settings will be active", + settings_file_path); + } goto out; } - uint32_t settings_array_count = 0; - bool has_multi_setting_file = false; + + // Because the file may contain either a "settings_array" or a single "settings" object, we need to create a cJSON so that we + // can iterate on both cases with common code + cJSON settings_iter_parent = {0}; + cJSON* settings_array = loader_cJSON_GetObjectItem(json, "settings_array"); cJSON* single_settings_object = loader_cJSON_GetObjectItem(json, "settings"); if (NULL != settings_array) { - has_multi_setting_file = true; - settings_array_count = loader_cJSON_GetArraySize(settings_array); + memcpy(&settings_iter_parent, settings_array, sizeof(cJSON)); } else if (NULL != single_settings_object) { - settings_array_count = 1; + settings_iter_parent.child = single_settings_object; + } else if (settings_array == NULL && single_settings_object) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "Loader settings file from %s missing required settings objects: Either one of the \"settings\" or " + "\"settings_array\" objects must be present - no loader settings will be active", + settings_file_path); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; } // Corresponds to the settings object that has no app keys - int global_settings_index = -1; + cJSON* global_settings = NULL; // Corresponds to the settings object which has a matching app key - int index_to_use = -1; + cJSON* settings_to_use = NULL; char current_process_path[1024]; bool valid_exe_path = NULL != loader_platform_executable_path(current_process_path, 1024); - for (int i = 0; i < (int)settings_array_count; i++) { - if (has_multi_setting_file) { - single_settings_object = loader_cJSON_GetArrayItem(settings_array, i); + cJSON* settings_object_iter = NULL; + cJSON_ArrayForEach(settings_object_iter, &settings_iter_parent) { + if (settings_object_iter->type != cJSON_Object) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "Loader settings file from %s has a settings element that is not an object", settings_file_path); + break; } - cJSON* app_keys = loader_cJSON_GetObjectItem(single_settings_object, "app_keys"); + + cJSON* app_keys = loader_cJSON_GetObjectItem(settings_object_iter, "app_keys"); if (NULL == app_keys) { - if (global_settings_index == -1) { - global_settings_index = i; // use the first 'global' settings that has no app keys as the global one + // use the first 'global' settings that has no app keys as the global one + if (global_settings == NULL) { + global_settings = settings_object_iter; } continue; - } else if (valid_exe_path) { - int app_key_count = loader_cJSON_GetArraySize(app_keys); - if (app_key_count == 0) { - continue; // empty array + } + // No sense iterating if we couldn't get the executable path + if (!valid_exe_path) { + break; + } + cJSON* app_key = NULL; + cJSON_ArrayForEach(app_key, app_keys) { + char* app_key_str = loader_cJSON_GetStringValue(app_key); + if (app_key_str && strcmp(current_process_path, app_key_str) == 0) { + settings_to_use = settings_object_iter; + break; } - for (int j = 0; j < app_key_count; j++) { - cJSON* app_key_json = loader_cJSON_GetArrayItem(app_keys, j); - if (NULL == app_key_json) { - continue; - } - char* app_key = loader_cJSON_Print(app_key_json); - if (NULL == app_key) { - continue; - } + } - if (strcmp(current_process_path, app_key) == 0) { - index_to_use = i; - } - loader_instance_heap_free(inst, app_key); - if (index_to_use == i) { - break; // break only after freeing the app key - } - } + // Break if we have found a matching current_process_path + if (NULL != settings_to_use) { + break; } } // No app specific settings match - either use global settings or exit - if (index_to_use == -1) { - if (global_settings_index == -1) { + if (settings_to_use == NULL) { + if (global_settings == NULL) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "Loader settings file from %s missing global settings and none of the app specific settings matched the " + "current application - no loader settings will be active", + settings_file_path); goto out; // No global settings were found - exit } else { - index_to_use = global_settings_index; // Global settings are present - use it - } - } - - // Now get the actual settings object to use - already have it if there is only one settings object - // If there are multiple settings, just need to set single_settings_object to the desired settings object - if (has_multi_setting_file) { - single_settings_object = loader_cJSON_GetArrayItem(settings_array, index_to_use); - if (NULL == single_settings_object) { - res = VK_ERROR_INITIALIZATION_FAILED; - goto out; + settings_to_use = global_settings; // Global settings are present - use it } } // optional - cJSON* stderr_filter = loader_cJSON_GetObjectItem(single_settings_object, "stderr_log"); + cJSON* stderr_filter = loader_cJSON_GetObjectItem(settings_to_use, "stderr_log"); if (NULL != stderr_filter) { struct loader_string_list stderr_log = {0}; - res = loader_parse_json_array_of_strings(inst, single_settings_object, "stderr_log", &stderr_log); + res = loader_parse_json_array_of_strings(inst, settings_to_use, "stderr_log", &stderr_log); if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { goto out; } @@ -409,30 +437,27 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } // optional - cJSON* logs_to_use = loader_cJSON_GetObjectItem(single_settings_object, "log_locations"); + cJSON* logs_to_use = loader_cJSON_GetObjectItem(settings_to_use, "log_locations"); if (NULL != logs_to_use) { - int log_count = loader_cJSON_GetArraySize(logs_to_use); - for (int i = 0; i < log_count; i++) { - cJSON* log_element = loader_cJSON_GetArrayItem(logs_to_use, i); + cJSON* log_element = NULL; + cJSON_ArrayForEach(log_element, logs_to_use) { // bool is_valid = true; - if (NULL != log_element) { - struct loader_string_list log_destinations = {0}; - res = loader_parse_json_array_of_strings(inst, log_element, "destinations", &log_destinations); - if (res != VK_SUCCESS) { - // is_valid = false; - } - free_string_list(inst, &log_destinations); - struct loader_string_list log_filters = {0}; - res = loader_parse_json_array_of_strings(inst, log_element, "filters", &log_filters); - if (res != VK_SUCCESS) { - // is_valid = false; - } - free_string_list(inst, &log_filters); + struct loader_string_list log_destinations = {0}; + res = loader_parse_json_array_of_strings(inst, log_element, "destinations", &log_destinations); + if (res != VK_SUCCESS) { + // is_valid = false; } + free_string_list(inst, &log_destinations); + struct loader_string_list log_filters = {0}; + res = loader_parse_json_array_of_strings(inst, log_element, "filters", &log_filters); + if (res != VK_SUCCESS) { + // is_valid = false; + } + free_string_list(inst, &log_filters); } } - res = parse_layer_configurations(inst, single_settings_object, loader_settings); + res = parse_layer_configurations(inst, settings_to_use, loader_settings); if (res != VK_SUCCESS) { goto out; } @@ -460,7 +485,7 @@ out: return res; } -VkResult update_global_loader_settings(void) { +TEST_FUNCTION_EXPORT VkResult update_global_loader_settings(void) { loader_settings settings = {0}; VkResult res = get_loader_settings(NULL, &settings); loader_platform_thread_lock_mutex(&global_loader_settings_lock); @@ -472,7 +497,7 @@ VkResult update_global_loader_settings(void) { } memcpy(&global_loader_settings, &settings, sizeof(loader_settings)); - if (global_loader_settings.settings_active) { + if (global_loader_settings.settings_active && global_loader_settings.debug_level > 0) { loader_set_global_debug_level(global_loader_settings.debug_level); } } @@ -514,8 +539,8 @@ void release_current_settings_lock(const struct loader_instance* inst) { } } -VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers, - bool* should_search_for_other_layers) { +TEST_FUNCTION_EXPORT VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers, + bool* should_search_for_other_layers) { VkResult res = VK_SUCCESS; *should_search_for_other_layers = true; // default to true @@ -577,6 +602,9 @@ VkResult get_settings_layers(const struct loader_instance* inst, struct loader_l continue; } + // Makes it possible to know if a new layer was added or not, since the only return value is VkResult + size_t count_before_adding = settings_layers->count; + local_res = loader_add_layer_properties(inst, settings_layers, json, layer_config->treat_as_implicit_manifest, layer_config->path); loader_cJSON_Delete(json); @@ -585,10 +613,15 @@ VkResult get_settings_layers(const struct loader_instance* inst, struct loader_l if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) { res = VK_ERROR_OUT_OF_HOST_MEMORY; goto out; + } else if (local_res != VK_SUCCESS || count_before_adding == settings_layers->count) { + // Indicates something was wrong with the layer, can't add it to the list + continue; } + struct loader_layer_properties* newly_added_layer = &settings_layers->list[settings_layers->count - 1]; newly_added_layer->settings_control_value = layer_config->control; - // If the manifest file found has a name that differs from the one in the settings, remove this layer from consideration + // If the manifest file found has a name that differs from the one in the settings, remove this layer from + // consideration bool should_remove = false; if (strncmp(newly_added_layer->info.layerName, layer_config->name, VK_MAX_EXTENSION_NAME_SIZE) != 0) { should_remove = true; @@ -616,16 +649,24 @@ out: } // Check if layers has an element with the same name. +// LAYER_CONTROL_OFF layers are missing some fields, just make sure the layerName is the same +// If layer_property is a meta layer, just make sure the layerName is the same +// Skip comparing to UNORDERED_LAYER_LOCATION // If layer_property is a regular layer, check if the lib_path is the same. -// If layer_property is a meta layer, just use the layerName +// Make sure that the lib_name pointers are non-null before calling strcmp. bool check_if_layer_is_in_list(struct loader_layer_list* layer_list, struct loader_layer_properties* layer_property) { // If the layer is a meta layer, just check against the name for (uint32_t i = 0; i < layer_list->count; i++) { if (0 == strncmp(layer_list->list[i].info.layerName, layer_property->info.layerName, VK_MAX_EXTENSION_NAME_SIZE)) { - if (0 == (layer_property->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) && - strcmp(layer_list->list[i].lib_name, layer_property->lib_name) == 0) { + if (layer_list->list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_OFF) { + return true; + } + if (VK_LAYER_TYPE_FLAG_META_LAYER == (layer_property->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { return true; } + if (layer_list->list[i].lib_name && layer_property->lib_name) { + return strcmp(layer_list->list[i].lib_name, layer_property->lib_name) == 0; + } } } return false; @@ -724,6 +765,7 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, if (vk_instance_layers_env != NULL) { vk_instance_layers_env_len = strlen(vk_instance_layers_env) + 1; vk_instance_layers_env_copy = loader_stack_alloc(vk_instance_layers_env_len); + memset(vk_instance_layers_env_copy, 0, vk_instance_layers_env_len); loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "env var \'%s\' defined and adding layers: %s", ENABLED_LAYERS_ENV, vk_instance_layers_env); @@ -732,6 +774,11 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, bool enable_layer = false; struct loader_layer_properties* props = &instance_layers->list[i]; + // Skip the sentinel unordered layer location + if (props->settings_control_value == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) { + continue; + } + // Do not enable the layer if the settings have it set as off if (props->settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_OFF) { continue; @@ -739,33 +786,45 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, // Force enable it based on settings if (props->settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON) { enable_layer = true; - } - - // Check if disable filter needs to skip the layer - if ((filters->disable_filter.disable_all || filters->disable_filter.disable_all_implicit || - check_name_matches_filter_environment_var(props->info.layerName, &filters->disable_filter.additional_filters)) && - !check_name_matches_filter_environment_var(props->info.layerName, &filters->allow_filter)) { - continue; + props->enabled_by_what = ENABLED_BY_WHAT_LOADER_SETTINGS_FILE; + } else { + // Check if disable filter needs to skip the layer + if ((filters->disable_filter.disable_all || filters->disable_filter.disable_all_implicit || + check_name_matches_filter_environment_var(props->info.layerName, &filters->disable_filter.additional_filters)) && + !check_name_matches_filter_environment_var(props->info.layerName, &filters->allow_filter)) { + // Report a message that we've forced off a layer if it would have been enabled normally. + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "Layer \"%s\" forced disabled because name matches filter of env var \'%s\'.", props->info.layerName, + VK_LAYERS_DISABLE_ENV_VAR); + + continue; + } } // Check the enable filter if (!enable_layer && check_name_matches_filter_environment_var(props->info.layerName, &filters->enable_filter)) { enable_layer = true; + props->enabled_by_what = ENABLED_BY_WHAT_VK_LOADER_LAYERS_ENABLE; + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "Layer \"%s\" forced enabled due to env var \'%s\'.", props->info.layerName, VK_LAYERS_ENABLE_ENV_VAR); } // First look for the old-fashion layers forced on with VK_INSTANCE_LAYERS if (!enable_layer && vk_instance_layers_env && vk_instance_layers_env_copy && vk_instance_layers_env_len > 0) { // Copy the env-var on each iteration, so that loader_get_next_path can correctly find the separators - // This solution only needs one stack allocation ahead of time rather than an allocation per layer in the env-var + // This solution only needs one stack allocation ahead of time rather than an allocation per layer in the + // env-var loader_strncpy(vk_instance_layers_env_copy, vk_instance_layers_env_len, vk_instance_layers_env, vk_instance_layers_env_len); - while (vk_instance_layers_env_copy && *vk_instance_layers_env_copy) { - char* next = loader_get_next_path(vk_instance_layers_env_copy); - if (0 == strcmp(vk_instance_layers_env_copy, props->info.layerName)) { + char* instance_layers_env_iter = vk_instance_layers_env_copy; + while (instance_layers_env_iter && *instance_layers_env_iter) { + char* next = loader_get_next_path(instance_layers_env_iter); + if (0 == strcmp(instance_layers_env_iter, props->info.layerName)) { enable_layer = true; + props->enabled_by_what = ENABLED_BY_WHAT_VK_INSTANCE_LAYERS; break; } - vk_instance_layers_env_copy = next; + instance_layers_env_iter = next; } } @@ -774,6 +833,7 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, for (uint32_t j = 0; j < app_enabled_name_count; j++) { if (strcmp(props->info.layerName, app_enabled_names[j]) == 0) { enable_layer = true; + props->enabled_by_what = ENABLED_BY_WHAT_IN_APPLICATION_API; break; } } @@ -783,6 +843,7 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, if (!enable_layer && (0 == (props->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)) && loader_implicit_layer_is_enabled(inst, filters, props)) { enable_layer = true; + props->enabled_by_what = ENABLED_BY_WHAT_IMPLICIT_LAYER; } if (enable_layer) { diff --git a/loader/settings.h b/loader/settings.h index e0ffe3a6e28a041167dfef58d88034cce012f847..4e776ff0ab0a79a14f8524dbcee1c3a7afa7b849 100644 --- a/loader/settings.h +++ b/loader/settings.h @@ -23,7 +23,6 @@ #pragma once -#include #include #include #include @@ -31,6 +30,7 @@ #include "vulkan/vulkan_core.h" #include "log.h" +#include "vk_loader_platform.h" struct loader_instance; struct loader_layer_list; @@ -84,7 +84,7 @@ void free_loader_settings(const struct loader_instance* inst, loader_settings* l void log_settings(const struct loader_instance* inst, loader_settings* settings); // Every global function needs to call this at startup to insure that -VkResult update_global_loader_settings(void); +TEST_FUNCTION_EXPORT VkResult update_global_loader_settings(void); // Needs to be called during startup - void init_global_loader_settings(void); @@ -95,8 +95,8 @@ bool should_skip_logging_global_messages(VkFlags msg_type); // Query the current settings (either global or per-instance) and return the list of layers contained within. // should_search_for_other_layers tells the caller if the settings file should be used exclusively for layer searching or not -VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers, - bool* should_search_for_other_layers); +TEST_FUNCTION_EXPORT VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers, + bool* should_search_for_other_layers); // Take the provided list of settings_layers and add in the layers from regular search paths // Only adds layers that aren't already present in the settings_layers and in the location of the diff --git a/loader/stack_allocation.h b/loader/stack_allocation.h index 0a2bbaa650e425aa5eb778377283529db2e91ddd..baf33667c6f6a401968b431c918822ddd562beaf 100644 --- a/loader/stack_allocation.h +++ b/loader/stack_allocation.h @@ -28,6 +28,8 @@ #pragma once +#include "vk_loader_platform.h" + #if defined(_WIN32) #include #elif defined(HAVE_ALLOCA_H) diff --git a/loader/terminator.c b/loader/terminator.c index 2218289a8d6a10242142ef410e897f07041a0b07..26f8f5ccc52a4fef6b5033f8de3d3c20e60b774b 100644 --- a/loader/terminator.c +++ b/loader/terminator.c @@ -31,10 +31,10 @@ // Terminators which have simple logic belong here, since they are mostly "pass through" // Function declarations are in vk_loader_extensions.h, thus not needed here -#include "allocation.h" #include "loader_common.h" #include "loader.h" #include "log.h" +#include "stack_allocation.h" // Terminators for 1.0 functions @@ -156,9 +156,11 @@ VKAPI_ATTR void VKAPI_CALL terminator_GetPhysicalDeviceFeatures2(VkPhysicalDevic // Write to the VkPhysicalDeviceFeatures2 struct icd_term->dispatch.GetPhysicalDeviceFeatures(phys_dev_term->phys_dev, &pFeatures->features); - const VkBaseInStructure *pNext = pFeatures->pNext; + void *pNext = pFeatures->pNext; while (pNext != NULL) { - switch (pNext->sType) { + VkBaseOutStructure pNext_in_structure = {0}; + memcpy(&pNext_in_structure, pNext, sizeof(VkBaseOutStructure)); + switch (pNext_in_structure.sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { // Skip the check if VK_KHR_multiview is enabled because it's a device extension // Write to the VkPhysicalDeviceMultiviewFeaturesKHR struct @@ -175,7 +177,7 @@ VKAPI_ATTR void VKAPI_CALL terminator_GetPhysicalDeviceFeatures2(VkPhysicalDevic "vkGetPhysicalDeviceFeatures2: Emulation found unrecognized structure type in pFeatures->pNext - " "this struct will be ignored"); - pNext = pNext->pNext; + pNext = pNext_in_structure.pNext; break; } } @@ -212,9 +214,11 @@ VKAPI_ATTR void VKAPI_CALL terminator_GetPhysicalDeviceProperties2(VkPhysicalDev // Write to the VkPhysicalDeviceProperties2 struct icd_term->dispatch.GetPhysicalDeviceProperties(phys_dev_term->phys_dev, &pProperties->properties); - const VkBaseInStructure *pNext = pProperties->pNext; + void *pNext = pProperties->pNext; while (pNext != NULL) { - switch (pNext->sType) { + VkBaseOutStructure pNext_in_structure = {0}; + memcpy(&pNext_in_structure, pNext, sizeof(VkBaseOutStructure)); + switch (pNext_in_structure.sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: { VkPhysicalDeviceIDPropertiesKHR *id_properties = (VkPhysicalDeviceIDPropertiesKHR *)pNext; @@ -238,7 +242,7 @@ VKAPI_ATTR void VKAPI_CALL terminator_GetPhysicalDeviceProperties2(VkPhysicalDev "vkGetPhysicalDeviceProperties2KHR: Emulation found unrecognized structure type in " "pProperties->pNext - this struct will be ignored"); - pNext = pNext->pNext; + pNext = pNext_in_structure.pNext; break; } } diff --git a/loader/trampoline.c b/loader/trampoline.c index f8e24a77ce937943014a5931ca3b05abe5ca7d70..143aca4a748005545d0087afab51548a2acc0ce9 100644 --- a/loader/trampoline.c +++ b/loader/trampoline.c @@ -138,8 +138,7 @@ LOADER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDev // sufficient if (!strcmp(name, "GetDeviceQueue2")) { struct loader_device *dev = NULL; - uint32_t index = 0; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL != icd_term && dev != NULL) { const struct loader_instance *inst = icd_term->this_instance; uint32_t api_version = @@ -178,15 +177,13 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert .version = VK_CURRENT_CHAIN_VERSION, .size = sizeof(chain_tail), }, - .pfnNextLayer = &terminator_EnumerateInstanceExtensionProperties, + .pfnNextLayer = &terminator_pre_instance_EnumerateInstanceExtensionProperties, .pNextLink = NULL, }; VkEnumerateInstanceExtensionPropertiesChain *chain_head = &chain_tail; // Get the implicit layers struct loader_layer_list layers = {0}; - loader_platform_dl_handle *libs = NULL; - size_t lib_count = 0; memset(&layers, 0, sizeof(layers)); struct loader_envvar_all_filters layer_filters = {0}; @@ -200,11 +197,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert return res; } - res = loader_init_library_list(&layers, &libs); - if (VK_SUCCESS != res) { - return res; - } - // Prepend layers onto the chain if they implement this entry point for (uint32_t i = 0; i < layers.count; ++i) { // Skip this layer if it doesn't expose the entry-point @@ -212,15 +204,12 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert continue; } - loader_platform_dl_handle layer_lib = loader_platform_open_library(layers.list[i].lib_name); - if (layer_lib == NULL) { - loader_log(NULL, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, - "%s: Unable to load implicit layer library \"%s\"", __FUNCTION__, layers.list[i].lib_name); + loader_open_layer_file(NULL, &layers.list[i]); + if (layers.list[i].lib_handle == NULL) { continue; } - libs[lib_count++] = layer_lib; - void *pfn = loader_platform_get_proc_address(layer_lib, + void *pfn = loader_platform_get_proc_address(layers.list[i].lib_handle, layers.list[i].pre_instance_functions.enumerate_instance_extension_properties); if (pfn == NULL) { loader_log(NULL, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, @@ -260,12 +249,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert loader_free(NULL, holder); } - // Close the dl handles - for (size_t i = 0; i < lib_count; ++i) { - loader_platform_close_library(libs[i]); - } - loader_free(NULL, libs); - return res; } @@ -284,15 +267,13 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( .version = VK_CURRENT_CHAIN_VERSION, .size = sizeof(chain_tail), }, - .pfnNextLayer = &terminator_EnumerateInstanceLayerProperties, + .pfnNextLayer = &terminator_pre_instance_EnumerateInstanceLayerProperties, .pNextLink = NULL, }; VkEnumerateInstanceLayerPropertiesChain *chain_head = &chain_tail; // Get the implicit layers struct loader_layer_list layers; - loader_platform_dl_handle *libs = NULL; - size_t lib_count = 0; memset(&layers, 0, sizeof(layers)); struct loader_envvar_all_filters layer_filters = {0}; @@ -306,11 +287,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( return res; } - res = loader_init_library_list(&layers, &libs); - if (VK_SUCCESS != res) { - return res; - } - // Prepend layers onto the chain if they implement this entry point for (uint32_t i = 0; i < layers.count; ++i) { // Skip this layer if it doesn't expose the entry-point @@ -318,16 +294,13 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( continue; } - loader_platform_dl_handle layer_lib = loader_platform_open_library(layers.list[i].lib_name); - if (layer_lib == NULL) { - loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "%s: Unable to load implicit layer library \"%s\"", __FUNCTION__, - layers.list[i].lib_name); + loader_open_layer_file(NULL, &layers.list[i]); + if (layers.list[i].lib_handle == NULL) { continue; } - libs[lib_count++] = layer_lib; - void *pfn = - loader_platform_get_proc_address(layer_lib, layers.list[i].pre_instance_functions.enumerate_instance_layer_properties); + void *pfn = loader_platform_get_proc_address(layers.list[i].lib_handle, + layers.list[i].pre_instance_functions.enumerate_instance_layer_properties); if (pfn == NULL) { loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "%s: Unable to resolve symbol \"%s\" in implicit layer library \"%s\"", __FUNCTION__, layers.list[i].pre_instance_functions.enumerate_instance_layer_properties, @@ -366,12 +339,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( loader_free(NULL, holder); } - // Close the dl handles - for (size_t i = 0; i < lib_count; ++i) { - loader_platform_close_library(libs[i]); - } - loader_free(NULL, libs); - return res; } @@ -397,15 +364,13 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion(uint32_t .version = VK_CURRENT_CHAIN_VERSION, .size = sizeof(chain_tail), }, - .pfnNextLayer = &terminator_EnumerateInstanceVersion, + .pfnNextLayer = &terminator_pre_instance_EnumerateInstanceVersion, .pNextLink = NULL, }; VkEnumerateInstanceVersionChain *chain_head = &chain_tail; // Get the implicit layers struct loader_layer_list layers; - loader_platform_dl_handle *libs = NULL; - size_t lib_count = 0; memset(&layers, 0, sizeof(layers)); struct loader_envvar_all_filters layer_filters = {0}; @@ -419,11 +384,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion(uint32_t return res; } - res = loader_init_library_list(&layers, &libs); - if (VK_SUCCESS != res) { - return res; - } - // Prepend layers onto the chain if they implement this entry point for (uint32_t i = 0; i < layers.count; ++i) { // Skip this layer if it doesn't expose the entry-point @@ -431,15 +391,13 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion(uint32_t continue; } - loader_platform_dl_handle layer_lib = loader_platform_open_library(layers.list[i].lib_name); - if (layer_lib == NULL) { - loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "%s: Unable to load implicit layer library \"%s\"", __FUNCTION__, - layers.list[i].lib_name); + loader_open_layer_file(NULL, &layers.list[i]); + if (layers.list[i].lib_handle == NULL) { continue; } - libs[lib_count++] = layer_lib; - void *pfn = loader_platform_get_proc_address(layer_lib, layers.list[i].pre_instance_functions.enumerate_instance_version); + void *pfn = loader_platform_get_proc_address(layers.list[i].lib_handle, + layers.list[i].pre_instance_functions.enumerate_instance_version); if (pfn == NULL) { loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "%s: Unable to resolve symbol \"%s\" in implicit layer library \"%s\"", __FUNCTION__, layers.list[i].pre_instance_functions.enumerate_instance_version, layers.list[i].lib_name); @@ -477,12 +435,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion(uint32_t loader_free(NULL, holder); } - // Close the dl handles - for (size_t i = 0; i < lib_count; ++i) { - loader_platform_close_library(libs[i]); - } - loader_free(NULL, libs); - return res; } @@ -598,7 +550,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCr } // Providing an apiVersion less than VK_API_VERSION_1_0 but greater than zero prevents the validation layers from starting - if (pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->apiVersion < VK_API_VERSION_1_0) { + if (pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->apiVersion != 0u && + pCreateInfo->pApplicationInfo->apiVersion < VK_API_VERSION_1_0) { loader_log(ptr_instance, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT, 0, "VkInstanceCreateInfo::pApplicationInfo::apiVersion has value of %u which is not permitted. If apiVersion is " "not 0, then it must be " @@ -768,6 +721,10 @@ out: free_loader_settings(ptr_instance, &ptr_instance->settings); + loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->surfaces_list); + loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->debug_utils_messengers_list); + loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->debug_report_callbacks_list); + loader_instance_heap_free(ptr_instance, ptr_instance->disp); // Remove any created VK_EXT_debug_report or VK_EXT_debug_utils items destroy_debug_callbacks_chain(ptr_instance, pAllocator); @@ -776,7 +733,7 @@ out: loader_destroy_pointer_layer_list(ptr_instance, &ptr_instance->app_activated_layer_list); loader_delete_layer_list_and_properties(ptr_instance, &ptr_instance->instance_layer_list); - loader_scanned_icd_clear(ptr_instance, &ptr_instance->icd_tramp_list); + loader_clear_scanned_icd_list(ptr_instance, &ptr_instance->icd_tramp_list); loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->ext_list); // Free any icd_terms that were created. @@ -788,6 +745,7 @@ out: // Call destroy Instance on each driver in case we successfully called down the chain but failed on // our way back out of it. if (icd_term->instance) { + loader_icd_close_objects(ptr_instance, icd_term); icd_term->dispatch.DestroyInstance(icd_term->instance, pAllocator); } icd_term->instance = VK_NULL_HANDLE; @@ -841,6 +799,10 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance, free_loader_settings(ptr_instance, &ptr_instance->settings); + loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->surfaces_list); + loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->debug_utils_messengers_list); + loader_destroy_generic_list(ptr_instance, (struct loader_generic_list *)&ptr_instance->debug_report_callbacks_list); + loader_destroy_pointer_layer_list(ptr_instance, &ptr_instance->expanded_activated_layer_list); loader_destroy_pointer_layer_list(ptr_instance, &ptr_instance->app_activated_layer_list); @@ -898,11 +860,15 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstan if (VK_SUCCESS != update_res) { res = update_res; } + + // Unloads any drivers that do not expose any physical devices - should save some address space + unload_drivers_without_physical_devices(inst); } out: loader_platform_thread_unlock_mutex(&loader_lock); + return res; } @@ -2606,15 +2572,14 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups( inst = loader_get_instance(instance); if (NULL == inst) { - loader_log( - NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, - "vkEnumeratePhysicalDeviceGroupsKHR: Invalid instance [VUID-vkEnumeratePhysicalDeviceGroups-instance-parameter]"); + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkEnumeratePhysicalDeviceGroups: Invalid instance [VUID-vkEnumeratePhysicalDeviceGroups-instance-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } if (NULL == pPhysicalDeviceGroupCount) { loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, - "vkEnumeratePhysicalDeviceGroupsKHR: Received NULL pointer for physical " + "vkEnumeratePhysicalDeviceGroups: Received NULL pointer for physical " "device group count return value."); res = VK_ERROR_INITIALIZATION_FAILED; goto out; @@ -3182,6 +3147,12 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceToolProperties(V VkPhysicalDeviceToolProperties *pToolProperties) { VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device(physicalDevice); const VkLayerInstanceDispatchTable *disp = loader_get_instance_layer_dispatch(physicalDevice); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPhysicalDeviceToolProperties: Invalid physicalDevice " + "[VUID-vkGetPhysicalDeviceToolProperties-physicalDevice-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } return disp->GetPhysicalDeviceToolProperties(unwrapped_phys_dev, pToolCount, pToolProperties); } @@ -3190,6 +3161,12 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceToolProperties(V LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo *pRenderingInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBeginRendering: Invalid commandBuffer " + "[VUID-vkCmdBeginRendering-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdBeginRendering(commandBuffer, pRenderingInfo); } @@ -3198,122 +3175,254 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers2(VkCommandBuffer const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes, const VkDeviceSize *pStrides) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBindVertexBuffers2: Invalid commandBuffer " + "[VUID-vkCmdBindVertexBuffers2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2 *pBlitImageInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBlitImage2: Invalid commandBuffer " + "[VUID-vkCmdBlitImage2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdBlitImage2(commandBuffer, pBlitImageInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2 *pCopyBufferInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdCopyBuffer2: Invalid commandBuffer " + "[VUID-vkCmdCopyBuffer2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdCopyBuffer2(commandBuffer, pCopyBufferInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdCopyBufferToImage2: Invalid commandBuffer " + "[VUID-vkCmdCopyBufferToImage2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdCopyImage2: Invalid commandBuffer " + "[VUID-vkCmdCopyImage2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdCopyImage2(commandBuffer, pCopyImageInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2 *pCopyImageToBufferInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdCopyImageToBuffer2: Invalid commandBuffer " + "[VUID-vkCmdCopyImageToBuffer2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdEndRendering(VkCommandBuffer commandBuffer) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdEndRendering: Invalid commandBuffer " + "[VUID-vkCmdEndRendering-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdEndRendering(commandBuffer); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPipelineBarrier2: Invalid commandBuffer " + "[VUID-vkCmdPipelineBarrier2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdPipelineBarrier2(commandBuffer, pDependencyInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdResetEvent2: Invalid commandBuffer " + "[VUID-vkCmdResetEvent2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdResetEvent2(commandBuffer, event, stageMask); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2 *pResolveImageInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdResolveImage2: Invalid commandBuffer " + "[VUID-vkCmdResolveImage2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdResolveImage2(commandBuffer, pResolveImageInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetCullMode: Invalid commandBuffer " + "[VUID-vkCmdSetCullMode-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetCullMode(commandBuffer, cullMode); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetDepthBiasEnable: Invalid commandBuffer " + "[VUID-vkCmdSetDepthBiasEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetDepthBiasEnable(commandBuffer, depthBiasEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetDepthBoundsTestEnable: Invalid commandBuffer " + "[VUID-vkCmdSetDepthBoundsTestEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetDepthBoundsTestEnable(commandBuffer, depthBoundsTestEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetDepthCompareOp: Invalid commandBuffer " + "[VUID-vkCmdSetDepthCompareOp-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetDepthCompareOp(commandBuffer, depthCompareOp); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetDepthTestEnable: Invalid commandBuffer " + "[VUID-vkCmdSetDepthTestEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetDepthTestEnable(commandBuffer, depthTestEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetDepthWriteEnable: Invalid commandBuffer " + "[VUID-vkCmdSetDepthWriteEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetDepthWriteEnable(commandBuffer, depthWriteEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo *pDependencyInfo) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetEvent2: Invalid commandBuffer " + "[VUID-vkCmdSetEvent2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetEvent2(commandBuffer, event, pDependencyInfo); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetFrontFace: Invalid commandBuffer " + "[VUID-vkCmdSetFrontFace-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetFrontFace(commandBuffer, frontFace); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetPrimitiveRestartEnable: Invalid commandBuffer " + "[VUID-vkCmdSetPrimitiveRestartEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetPrimitiveTopology: Invalid commandBuffer " + "[VUID-vkCmdSetPrimitiveTopology-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetPrimitiveTopology(commandBuffer, primitiveTopology); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetRasterizerDiscardEnable: Invalid commandBuffer " + "[VUID-vkCmdSetRasterizerDiscardEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetRasterizerDiscardEnable(commandBuffer, rasterizerDiscardEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D *pScissors) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetScissorWithCount: Invalid commandBuffer " + "[VUID-vkCmdSetScissorWithCount-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors); } @@ -3321,29 +3430,59 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilOp(VkCommandBuffer comma VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetStencilOp: Invalid commandBuffer " + "[VUID-vkCmdSetStencilOp-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetStencilOp(commandBuffer, faceMask, failOp, passOp, depthFailOp, compareOp); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetStencilTestEnable: Invalid commandBuffer " + "[VUID-vkCmdSetStencilTestEnable-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetStencilTestEnable(commandBuffer, stencilTestEnable); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport *pViewports) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetViewportWithCount: Invalid commandBuffer " + "[VUID-vkCmdSetViewportWithCount-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdSetViewportWithCount(commandBuffer, viewportCount, pViewports); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, const VkDependencyInfo *pDependencyInfos) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdWaitEvents2: Invalid commandBuffer " + "[VUID-vkCmdWaitEvents2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) { const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdWriteTimestamp2: Invalid commandBuffer " + "[VUID-vkCmdWriteTimestamp2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->CmdWriteTimestamp2(commandBuffer, stage, queryPool, query); } @@ -3352,12 +3491,24 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreatePrivateDataSlot(VkDevice de const VkAllocationCallbacks *pAllocator, VkPrivateDataSlot *pPrivateDataSlot) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCreatePrivateDataSlot: Invalid device " + "[VUID-vkCreatePrivateDataSlot-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } return disp->CreatePrivateDataSlot(device, pCreateInfo, pAllocator, pPrivateDataSlot); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks *pAllocator) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkDestroyPrivateDataSlot: Invalid device " + "[VUID-vkDestroyPrivateDataSlot-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->DestroyPrivateDataSlot(device, privateDataSlot, pAllocator); } @@ -3365,6 +3516,12 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetDeviceBufferMemoryRequirements(VkD const VkDeviceBufferMemoryRequirements *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetDeviceBufferMemoryRequirements: Invalid device " + "[VUID-vkGetDeviceBufferMemoryRequirements-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->GetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements); } @@ -3372,6 +3529,12 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageMemoryRequirements(VkDe const VkDeviceImageMemoryRequirements *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetDeviceImageMemoryRequirements: Invalid device " + "[VUID-vkGetDeviceImageMemoryRequirements-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->GetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements); } @@ -3379,23 +3542,286 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageSparseMemoryRequirement VkDevice device, const VkDeviceImageMemoryRequirements *pInfo, uint32_t *pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetDeviceImageSparseMemoryRequirements: Invalid device " + "[VUID-vkGetDeviceImageSparseMemoryRequirements-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->GetDeviceImageSparseMemoryRequirements(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); } LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t *pData) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetPrivateData: Invalid device " + "[VUID-vkGetPrivateData-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } disp->GetPrivateData(device, objectType, objectHandle, privateDataSlot, pData); } LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkSetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) { const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkSetPrivateData: Invalid device " + "[VUID-vkSetPrivateData-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } return disp->SetPrivateData(device, objectType, objectHandle, privateDataSlot, data); } LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 *pSubmits, VkFence fence) { const VkLayerDispatchTable *disp = loader_get_dispatch(queue); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkQueueSubmit2: Invalid queue " + "[VUID-vkQueueSubmit2-queue-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } return disp->QueueSubmit2(queue, submitCount, pSubmits, fence); } + +// ---- Vulkan core 1.4 trampolines + +// Instance + +// Device + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetRenderingAreaGranularity(VkDevice device, + const VkRenderingAreaInfo *pRenderingAreaInfo, + VkExtent2D *pGranularity) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetRenderingAreaGranularity: Invalid device " + "[VUID-vkGetRenderingAreaGranularity-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetRenderingAreaGranularity(device, pRenderingAreaInfo, pGranularity); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSet(VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, + uint32_t set, uint32_t descriptorWriteCount, + const VkWriteDescriptorSet *pDescriptorWrites) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPushDescriptorSet: Invalid commandBuffer " + "[VUID-vkCmdPushDescriptorSet-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdPushDescriptorSet(commandBuffer, pipelineBindPoint, layout, set, descriptorWriteCount, pDescriptorWrites); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, + VkDescriptorUpdateTemplate descriptorUpdateTemplate, + VkPipelineLayout layout, uint32_t set, + const void *pData) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPushDescriptorSetWithTemplate: Invalid commandBuffer " + "[VUID-vkCmdPushDescriptorSetWithTemplate-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdPushDescriptorSetWithTemplate(commandBuffer, descriptorUpdateTemplate, layout, set, pData); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, + uint16_t lineStipplePattern) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetLineStipple: Invalid commandBuffer " + "[VUID-vkCmdSetLineStipple-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetLineStipple(commandBuffer, lineStippleFactor, lineStipplePattern); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, + VkDeviceSize size, VkIndexType indexType) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBindIndexBuffer2: Invalid commandBuffer " + "[VUID-vkCmdBindIndexBuffer2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdBindIndexBuffer2(commandBuffer, buffer, offset, size, indexType); +} + +LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCopyMemoryToImage(VkDevice device, + const VkCopyMemoryToImageInfo *pCopyMemoryToImageInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCopyMemoryToImage: Invalid device " + "[VUID-vkCopyMemoryToImage-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->CopyMemoryToImage(device, pCopyMemoryToImageInfo); +} + +LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCopyImageToMemory(VkDevice device, + const VkCopyImageToMemoryInfo *pCopyImageToMemoryInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCopyImageToMemory: Invalid device " + "[VUID-vkCopyImageToMemory-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->CopyImageToMemory(device, pCopyImageToMemoryInfo); +} + +LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCopyImageToImage(VkDevice device, + const VkCopyImageToImageInfo *pCopyImageToImageInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCopyImageToImage: Invalid device " + "[VUID-vkCopyImageToImage-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->CopyImageToImage(device, pCopyImageToImageInfo); +} + +LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkTransitionImageLayout(VkDevice device, uint32_t transitionCount, + const VkHostImageLayoutTransitionInfo *pTransitions) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkTransitionImageLayout: Invalid device " + "[VUID-vkTransitionImageLayout-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->TransitionImageLayout(device, transitionCount, pTransitions); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageSubresourceLayout(VkDevice device, + const VkDeviceImageSubresourceInfo *pInfo, + VkSubresourceLayout2 *pLayout) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetDeviceImageSubresourceLayout: Invalid device " + "[VUID-vkGetDeviceImageSubresourceLayout-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetDeviceImageSubresourceLayout(device, pInfo, pLayout); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout2(VkDevice device, VkImage image, + const VkImageSubresource2 *pSubresource, + VkSubresourceLayout2 *pLayout) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkGetImageSubresourceLayout2: Invalid device " + "[VUID-vkGetImageSubresourceLayout2-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->GetImageSubresourceLayout2(device, image, pSubresource, pLayout); +} + +LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory2(VkDevice device, const VkMemoryMapInfo *pMemoryMapInfo, void **ppData) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkMapMemory2: Invalid device " + "[VUID-vkMapMemory2-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->MapMemory2(device, pMemoryMapInfo, ppData); +} + +LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkUnmapMemory2(VkDevice device, const VkMemoryUnmapInfo *pMemoryUnmapInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(device); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkUnmapMemory2: Invalid device " + "[VUID-vkUnmapMemory2-device-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + return disp->UnmapMemory2(device, pMemoryUnmapInfo); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBindDescriptorSets2(VkCommandBuffer commandBuffer, + const VkBindDescriptorSetsInfo *pBindDescriptorSetsInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdBindDescriptorSets2: Invalid commandBuffer " + "[VUID-vkCmdBindDescriptorSets2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdBindDescriptorSets2(commandBuffer, pBindDescriptorSetsInfo); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants2(VkCommandBuffer commandBuffer, + const VkPushConstantsInfo *pPushConstantsInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPushConstants2: Invalid commandBuffer " + "[VUID-vkCmdPushConstants2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdPushConstants2(commandBuffer, pPushConstantsInfo); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSet2(VkCommandBuffer commandBuffer, + const VkPushDescriptorSetInfo *pPushDescriptorSetInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPushDescriptorSet2: Invalid commandBuffer " + "[VUID-vkCmdPushDescriptorSet2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdPushDescriptorSet2(commandBuffer, pPushDescriptorSetInfo); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplate2( + VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo *pPushDescriptorSetWithTemplateInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdPushDescriptorSetWithTemplate2: Invalid commandBuffer " + "[VUID-vkCmdPushDescriptorSetWithTemplate2-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdPushDescriptorSetWithTemplate2(commandBuffer, pPushDescriptorSetWithTemplateInfo); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL +vkCmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo *pLocationInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetRenderingAttachmentLocations: Invalid commandBuffer " + "[VUID-vkCmdSetRenderingAttachmentLocations-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetRenderingAttachmentLocations(commandBuffer, pLocationInfo); +} + +LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdSetRenderingInputAttachmentIndices( + VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo *pInputAttachmentIndexInfo) { + const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer); + if (NULL == disp) { + loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, + "vkCmdSetRenderingInputAttachmentIndices: Invalid commandBuffer " + "[VUID-vkCmdSetRenderingInputAttachmentIndices-commandBuffer-parameter]"); + abort(); /* Intentionally fail so user can correct issue. */ + } + disp->CmdSetRenderingInputAttachmentIndices(commandBuffer, pInputAttachmentIndexInfo); +} diff --git a/loader/unknown_ext_chain.c b/loader/unknown_ext_chain.c deleted file mode 100644 index 4bcf1d527d912f9c4074852bcd2319fb16e05394..0000000000000000000000000000000000000000 --- a/loader/unknown_ext_chain.c +++ /dev/null @@ -1,820 +0,0 @@ -/* - * Copyright (c) 2017-2021 The Khronos Group Inc. - * Copyright (c) 2017-2021 Valve Corporation - * Copyright (c) 2017-2021 LunarG, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Author Jon Ashburn - * Author: Lenny Komow - * Author: Charles Giessen - */ - -// This code is used to pass on physical device extensions through the call chain. It must do this without creating a stack frame, -// because the actual parameters of the call are not known. Since the first parameter is known to be a VkPhysicalDevice, it can -// unwrap the physical device, overwriting the wrapped device, and then jump to the next function in the call chain. This code -// attempts to accomplish this by relying on tail-call optimizations, but there is no guarantee that this will work. As a result, -// this code is only compiled on systems where an assembly alternative has not been written. - -#include "loader.h" -#include "log.h" - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC optimize(3) // force gcc to use tail-calls -#endif - -// Trampoline function macro for unknown physical device extension command. -#define PhysDevExtTramp(num) \ - VKAPI_ATTR void VKAPI_CALL vkPhysDevExtTramp##num(VkPhysicalDevice physical_device) { \ - const struct loader_instance_dispatch_table *disp; \ - disp = loader_get_instance_dispatch(physical_device); \ - disp->phys_dev_ext[num](loader_unwrap_physical_device(physical_device)); \ - } - -// Terminator function macro for unknown physical device extension command. -#define PhysDevExtTermin(num) \ - VKAPI_ATTR void VKAPI_CALL vkPhysDevExtTermin##num(VkPhysicalDevice physical_device) { \ - struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)physical_device; \ - struct loader_icd_term *icd_term = phys_dev_term->this_icd_term; \ - struct loader_instance *inst = (struct loader_instance *)icd_term->this_instance; \ - if (NULL == icd_term->phys_dev_ext[num]) { \ - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "Function %s not supported for this physical device", \ - inst->phys_dev_ext_disp_functions[num]); \ - } \ - icd_term->phys_dev_ext[num](phys_dev_term->phys_dev); \ - } - -// Trampoline function macro for unknown physical device extension command. -#define DevExtTramp(num) \ - VKAPI_ATTR void VKAPI_CALL vkdev_ext##num(VkDevice device) { \ - const struct loader_dev_dispatch_table *disp; \ - disp = loader_get_dev_dispatch(device); \ - disp->ext_dispatch[num](device); \ - } - -// clang-format off -// Instantiations of the trampoline -PhysDevExtTramp(0) -PhysDevExtTramp(1) -PhysDevExtTramp(2) -PhysDevExtTramp(3) -PhysDevExtTramp(4) -PhysDevExtTramp(5) -PhysDevExtTramp(6) -PhysDevExtTramp(7) -PhysDevExtTramp(8) -PhysDevExtTramp(9) -PhysDevExtTramp(10) -PhysDevExtTramp(11) -PhysDevExtTramp(12) -PhysDevExtTramp(13) -PhysDevExtTramp(14) -PhysDevExtTramp(15) -PhysDevExtTramp(16) -PhysDevExtTramp(17) -PhysDevExtTramp(18) -PhysDevExtTramp(19) -PhysDevExtTramp(20) -PhysDevExtTramp(21) -PhysDevExtTramp(22) -PhysDevExtTramp(23) -PhysDevExtTramp(24) -PhysDevExtTramp(25) -PhysDevExtTramp(26) -PhysDevExtTramp(27) -PhysDevExtTramp(28) -PhysDevExtTramp(29) -PhysDevExtTramp(30) -PhysDevExtTramp(31) -PhysDevExtTramp(32) -PhysDevExtTramp(33) -PhysDevExtTramp(34) -PhysDevExtTramp(35) -PhysDevExtTramp(36) -PhysDevExtTramp(37) -PhysDevExtTramp(38) -PhysDevExtTramp(39) -PhysDevExtTramp(40) -PhysDevExtTramp(41) -PhysDevExtTramp(42) -PhysDevExtTramp(43) -PhysDevExtTramp(44) -PhysDevExtTramp(45) -PhysDevExtTramp(46) -PhysDevExtTramp(47) -PhysDevExtTramp(48) -PhysDevExtTramp(49) -PhysDevExtTramp(50) -PhysDevExtTramp(51) -PhysDevExtTramp(52) -PhysDevExtTramp(53) -PhysDevExtTramp(54) -PhysDevExtTramp(55) -PhysDevExtTramp(56) -PhysDevExtTramp(57) -PhysDevExtTramp(58) -PhysDevExtTramp(59) -PhysDevExtTramp(60) -PhysDevExtTramp(61) -PhysDevExtTramp(62) -PhysDevExtTramp(63) -PhysDevExtTramp(64) -PhysDevExtTramp(65) -PhysDevExtTramp(66) -PhysDevExtTramp(67) -PhysDevExtTramp(68) -PhysDevExtTramp(69) -PhysDevExtTramp(70) -PhysDevExtTramp(71) -PhysDevExtTramp(72) -PhysDevExtTramp(73) -PhysDevExtTramp(74) -PhysDevExtTramp(75) -PhysDevExtTramp(76) -PhysDevExtTramp(77) -PhysDevExtTramp(78) -PhysDevExtTramp(79) -PhysDevExtTramp(80) -PhysDevExtTramp(81) -PhysDevExtTramp(82) -PhysDevExtTramp(83) -PhysDevExtTramp(84) -PhysDevExtTramp(85) -PhysDevExtTramp(86) -PhysDevExtTramp(87) -PhysDevExtTramp(88) -PhysDevExtTramp(89) -PhysDevExtTramp(90) -PhysDevExtTramp(91) -PhysDevExtTramp(92) -PhysDevExtTramp(93) -PhysDevExtTramp(94) -PhysDevExtTramp(95) -PhysDevExtTramp(96) -PhysDevExtTramp(97) -PhysDevExtTramp(98) -PhysDevExtTramp(99) -PhysDevExtTramp(100) -PhysDevExtTramp(101) -PhysDevExtTramp(102) -PhysDevExtTramp(103) -PhysDevExtTramp(104) -PhysDevExtTramp(105) -PhysDevExtTramp(106) -PhysDevExtTramp(107) -PhysDevExtTramp(108) -PhysDevExtTramp(109) -PhysDevExtTramp(110) -PhysDevExtTramp(111) -PhysDevExtTramp(112) -PhysDevExtTramp(113) -PhysDevExtTramp(114) -PhysDevExtTramp(115) -PhysDevExtTramp(116) -PhysDevExtTramp(117) -PhysDevExtTramp(118) -PhysDevExtTramp(119) -PhysDevExtTramp(120) -PhysDevExtTramp(121) -PhysDevExtTramp(122) -PhysDevExtTramp(123) -PhysDevExtTramp(124) -PhysDevExtTramp(125) -PhysDevExtTramp(126) -PhysDevExtTramp(127) -PhysDevExtTramp(128) -PhysDevExtTramp(129) -PhysDevExtTramp(130) -PhysDevExtTramp(131) -PhysDevExtTramp(132) -PhysDevExtTramp(133) -PhysDevExtTramp(134) -PhysDevExtTramp(135) -PhysDevExtTramp(136) -PhysDevExtTramp(137) -PhysDevExtTramp(138) -PhysDevExtTramp(139) -PhysDevExtTramp(140) -PhysDevExtTramp(141) -PhysDevExtTramp(142) -PhysDevExtTramp(143) -PhysDevExtTramp(144) -PhysDevExtTramp(145) -PhysDevExtTramp(146) -PhysDevExtTramp(147) -PhysDevExtTramp(148) -PhysDevExtTramp(149) -PhysDevExtTramp(150) -PhysDevExtTramp(151) -PhysDevExtTramp(152) -PhysDevExtTramp(153) -PhysDevExtTramp(154) -PhysDevExtTramp(155) -PhysDevExtTramp(156) -PhysDevExtTramp(157) -PhysDevExtTramp(158) -PhysDevExtTramp(159) -PhysDevExtTramp(160) -PhysDevExtTramp(161) -PhysDevExtTramp(162) -PhysDevExtTramp(163) -PhysDevExtTramp(164) -PhysDevExtTramp(165) -PhysDevExtTramp(166) -PhysDevExtTramp(167) -PhysDevExtTramp(168) -PhysDevExtTramp(169) -PhysDevExtTramp(170) -PhysDevExtTramp(171) -PhysDevExtTramp(172) -PhysDevExtTramp(173) -PhysDevExtTramp(174) -PhysDevExtTramp(175) -PhysDevExtTramp(176) -PhysDevExtTramp(177) -PhysDevExtTramp(178) -PhysDevExtTramp(179) -PhysDevExtTramp(180) -PhysDevExtTramp(181) -PhysDevExtTramp(182) -PhysDevExtTramp(183) -PhysDevExtTramp(184) -PhysDevExtTramp(185) -PhysDevExtTramp(186) -PhysDevExtTramp(187) -PhysDevExtTramp(188) -PhysDevExtTramp(189) -PhysDevExtTramp(190) -PhysDevExtTramp(191) -PhysDevExtTramp(192) -PhysDevExtTramp(193) -PhysDevExtTramp(194) -PhysDevExtTramp(195) -PhysDevExtTramp(196) -PhysDevExtTramp(197) -PhysDevExtTramp(198) -PhysDevExtTramp(199) -PhysDevExtTramp(200) -PhysDevExtTramp(201) -PhysDevExtTramp(202) -PhysDevExtTramp(203) -PhysDevExtTramp(204) -PhysDevExtTramp(205) -PhysDevExtTramp(206) -PhysDevExtTramp(207) -PhysDevExtTramp(208) -PhysDevExtTramp(209) -PhysDevExtTramp(210) -PhysDevExtTramp(211) -PhysDevExtTramp(212) -PhysDevExtTramp(213) -PhysDevExtTramp(214) -PhysDevExtTramp(215) -PhysDevExtTramp(216) -PhysDevExtTramp(217) -PhysDevExtTramp(218) -PhysDevExtTramp(219) -PhysDevExtTramp(220) -PhysDevExtTramp(221) -PhysDevExtTramp(222) -PhysDevExtTramp(223) -PhysDevExtTramp(224) -PhysDevExtTramp(225) -PhysDevExtTramp(226) -PhysDevExtTramp(227) -PhysDevExtTramp(228) -PhysDevExtTramp(229) -PhysDevExtTramp(230) -PhysDevExtTramp(231) -PhysDevExtTramp(232) -PhysDevExtTramp(233) -PhysDevExtTramp(234) -PhysDevExtTramp(235) -PhysDevExtTramp(236) -PhysDevExtTramp(237) -PhysDevExtTramp(238) -PhysDevExtTramp(239) -PhysDevExtTramp(240) -PhysDevExtTramp(241) -PhysDevExtTramp(242) -PhysDevExtTramp(243) -PhysDevExtTramp(244) -PhysDevExtTramp(245) -PhysDevExtTramp(246) -PhysDevExtTramp(247) -PhysDevExtTramp(248) -PhysDevExtTramp(249) - -// Instantiations of the terminator -PhysDevExtTermin(0) -PhysDevExtTermin(1) -PhysDevExtTermin(2) -PhysDevExtTermin(3) -PhysDevExtTermin(4) -PhysDevExtTermin(5) -PhysDevExtTermin(6) -PhysDevExtTermin(7) -PhysDevExtTermin(8) -PhysDevExtTermin(9) -PhysDevExtTermin(10) -PhysDevExtTermin(11) -PhysDevExtTermin(12) -PhysDevExtTermin(13) -PhysDevExtTermin(14) -PhysDevExtTermin(15) -PhysDevExtTermin(16) -PhysDevExtTermin(17) -PhysDevExtTermin(18) -PhysDevExtTermin(19) -PhysDevExtTermin(20) -PhysDevExtTermin(21) -PhysDevExtTermin(22) -PhysDevExtTermin(23) -PhysDevExtTermin(24) -PhysDevExtTermin(25) -PhysDevExtTermin(26) -PhysDevExtTermin(27) -PhysDevExtTermin(28) -PhysDevExtTermin(29) -PhysDevExtTermin(30) -PhysDevExtTermin(31) -PhysDevExtTermin(32) -PhysDevExtTermin(33) -PhysDevExtTermin(34) -PhysDevExtTermin(35) -PhysDevExtTermin(36) -PhysDevExtTermin(37) -PhysDevExtTermin(38) -PhysDevExtTermin(39) -PhysDevExtTermin(40) -PhysDevExtTermin(41) -PhysDevExtTermin(42) -PhysDevExtTermin(43) -PhysDevExtTermin(44) -PhysDevExtTermin(45) -PhysDevExtTermin(46) -PhysDevExtTermin(47) -PhysDevExtTermin(48) -PhysDevExtTermin(49) -PhysDevExtTermin(50) -PhysDevExtTermin(51) -PhysDevExtTermin(52) -PhysDevExtTermin(53) -PhysDevExtTermin(54) -PhysDevExtTermin(55) -PhysDevExtTermin(56) -PhysDevExtTermin(57) -PhysDevExtTermin(58) -PhysDevExtTermin(59) -PhysDevExtTermin(60) -PhysDevExtTermin(61) -PhysDevExtTermin(62) -PhysDevExtTermin(63) -PhysDevExtTermin(64) -PhysDevExtTermin(65) -PhysDevExtTermin(66) -PhysDevExtTermin(67) -PhysDevExtTermin(68) -PhysDevExtTermin(69) -PhysDevExtTermin(70) -PhysDevExtTermin(71) -PhysDevExtTermin(72) -PhysDevExtTermin(73) -PhysDevExtTermin(74) -PhysDevExtTermin(75) -PhysDevExtTermin(76) -PhysDevExtTermin(77) -PhysDevExtTermin(78) -PhysDevExtTermin(79) -PhysDevExtTermin(80) -PhysDevExtTermin(81) -PhysDevExtTermin(82) -PhysDevExtTermin(83) -PhysDevExtTermin(84) -PhysDevExtTermin(85) -PhysDevExtTermin(86) -PhysDevExtTermin(87) -PhysDevExtTermin(88) -PhysDevExtTermin(89) -PhysDevExtTermin(90) -PhysDevExtTermin(91) -PhysDevExtTermin(92) -PhysDevExtTermin(93) -PhysDevExtTermin(94) -PhysDevExtTermin(95) -PhysDevExtTermin(96) -PhysDevExtTermin(97) -PhysDevExtTermin(98) -PhysDevExtTermin(99) -PhysDevExtTermin(100) -PhysDevExtTermin(101) -PhysDevExtTermin(102) -PhysDevExtTermin(103) -PhysDevExtTermin(104) -PhysDevExtTermin(105) -PhysDevExtTermin(106) -PhysDevExtTermin(107) -PhysDevExtTermin(108) -PhysDevExtTermin(109) -PhysDevExtTermin(110) -PhysDevExtTermin(111) -PhysDevExtTermin(112) -PhysDevExtTermin(113) -PhysDevExtTermin(114) -PhysDevExtTermin(115) -PhysDevExtTermin(116) -PhysDevExtTermin(117) -PhysDevExtTermin(118) -PhysDevExtTermin(119) -PhysDevExtTermin(120) -PhysDevExtTermin(121) -PhysDevExtTermin(122) -PhysDevExtTermin(123) -PhysDevExtTermin(124) -PhysDevExtTermin(125) -PhysDevExtTermin(126) -PhysDevExtTermin(127) -PhysDevExtTermin(128) -PhysDevExtTermin(129) -PhysDevExtTermin(130) -PhysDevExtTermin(131) -PhysDevExtTermin(132) -PhysDevExtTermin(133) -PhysDevExtTermin(134) -PhysDevExtTermin(135) -PhysDevExtTermin(136) -PhysDevExtTermin(137) -PhysDevExtTermin(138) -PhysDevExtTermin(139) -PhysDevExtTermin(140) -PhysDevExtTermin(141) -PhysDevExtTermin(142) -PhysDevExtTermin(143) -PhysDevExtTermin(144) -PhysDevExtTermin(145) -PhysDevExtTermin(146) -PhysDevExtTermin(147) -PhysDevExtTermin(148) -PhysDevExtTermin(149) -PhysDevExtTermin(150) -PhysDevExtTermin(151) -PhysDevExtTermin(152) -PhysDevExtTermin(153) -PhysDevExtTermin(154) -PhysDevExtTermin(155) -PhysDevExtTermin(156) -PhysDevExtTermin(157) -PhysDevExtTermin(158) -PhysDevExtTermin(159) -PhysDevExtTermin(160) -PhysDevExtTermin(161) -PhysDevExtTermin(162) -PhysDevExtTermin(163) -PhysDevExtTermin(164) -PhysDevExtTermin(165) -PhysDevExtTermin(166) -PhysDevExtTermin(167) -PhysDevExtTermin(168) -PhysDevExtTermin(169) -PhysDevExtTermin(170) -PhysDevExtTermin(171) -PhysDevExtTermin(172) -PhysDevExtTermin(173) -PhysDevExtTermin(174) -PhysDevExtTermin(175) -PhysDevExtTermin(176) -PhysDevExtTermin(177) -PhysDevExtTermin(178) -PhysDevExtTermin(179) -PhysDevExtTermin(180) -PhysDevExtTermin(181) -PhysDevExtTermin(182) -PhysDevExtTermin(183) -PhysDevExtTermin(184) -PhysDevExtTermin(185) -PhysDevExtTermin(186) -PhysDevExtTermin(187) -PhysDevExtTermin(188) -PhysDevExtTermin(189) -PhysDevExtTermin(190) -PhysDevExtTermin(191) -PhysDevExtTermin(192) -PhysDevExtTermin(193) -PhysDevExtTermin(194) -PhysDevExtTermin(195) -PhysDevExtTermin(196) -PhysDevExtTermin(197) -PhysDevExtTermin(198) -PhysDevExtTermin(199) -PhysDevExtTermin(200) -PhysDevExtTermin(201) -PhysDevExtTermin(202) -PhysDevExtTermin(203) -PhysDevExtTermin(204) -PhysDevExtTermin(205) -PhysDevExtTermin(206) -PhysDevExtTermin(207) -PhysDevExtTermin(208) -PhysDevExtTermin(209) -PhysDevExtTermin(210) -PhysDevExtTermin(211) -PhysDevExtTermin(212) -PhysDevExtTermin(213) -PhysDevExtTermin(214) -PhysDevExtTermin(215) -PhysDevExtTermin(216) -PhysDevExtTermin(217) -PhysDevExtTermin(218) -PhysDevExtTermin(219) -PhysDevExtTermin(220) -PhysDevExtTermin(221) -PhysDevExtTermin(222) -PhysDevExtTermin(223) -PhysDevExtTermin(224) -PhysDevExtTermin(225) -PhysDevExtTermin(226) -PhysDevExtTermin(227) -PhysDevExtTermin(228) -PhysDevExtTermin(229) -PhysDevExtTermin(230) -PhysDevExtTermin(231) -PhysDevExtTermin(232) -PhysDevExtTermin(233) -PhysDevExtTermin(234) -PhysDevExtTermin(235) -PhysDevExtTermin(236) -PhysDevExtTermin(237) -PhysDevExtTermin(238) -PhysDevExtTermin(239) -PhysDevExtTermin(240) -PhysDevExtTermin(241) -PhysDevExtTermin(242) -PhysDevExtTermin(243) -PhysDevExtTermin(244) -PhysDevExtTermin(245) -PhysDevExtTermin(246) -PhysDevExtTermin(247) -PhysDevExtTermin(248) -PhysDevExtTermin(249) - -// Instantiations of the device trampoline -DevExtTramp(0) -DevExtTramp(1) -DevExtTramp(2) -DevExtTramp(3) -DevExtTramp(4) -DevExtTramp(5) -DevExtTramp(6) -DevExtTramp(7) -DevExtTramp(8) -DevExtTramp(9) -DevExtTramp(10) -DevExtTramp(11) -DevExtTramp(12) -DevExtTramp(13) -DevExtTramp(14) -DevExtTramp(15) -DevExtTramp(16) -DevExtTramp(17) -DevExtTramp(18) -DevExtTramp(19) -DevExtTramp(20) -DevExtTramp(21) -DevExtTramp(22) -DevExtTramp(23) -DevExtTramp(24) -DevExtTramp(25) -DevExtTramp(26) -DevExtTramp(27) -DevExtTramp(28) -DevExtTramp(29) -DevExtTramp(30) -DevExtTramp(31) -DevExtTramp(32) -DevExtTramp(33) -DevExtTramp(34) -DevExtTramp(35) -DevExtTramp(36) -DevExtTramp(37) -DevExtTramp(38) -DevExtTramp(39) -DevExtTramp(40) -DevExtTramp(41) -DevExtTramp(42) -DevExtTramp(43) -DevExtTramp(44) -DevExtTramp(45) -DevExtTramp(46) -DevExtTramp(47) -DevExtTramp(48) -DevExtTramp(49) -DevExtTramp(50) -DevExtTramp(51) -DevExtTramp(52) -DevExtTramp(53) -DevExtTramp(54) -DevExtTramp(55) -DevExtTramp(56) -DevExtTramp(57) -DevExtTramp(58) -DevExtTramp(59) -DevExtTramp(60) -DevExtTramp(61) -DevExtTramp(62) -DevExtTramp(63) -DevExtTramp(64) -DevExtTramp(65) -DevExtTramp(66) -DevExtTramp(67) -DevExtTramp(68) -DevExtTramp(69) -DevExtTramp(70) -DevExtTramp(71) -DevExtTramp(72) -DevExtTramp(73) -DevExtTramp(74) -DevExtTramp(75) -DevExtTramp(76) -DevExtTramp(77) -DevExtTramp(78) -DevExtTramp(79) -DevExtTramp(80) -DevExtTramp(81) -DevExtTramp(82) -DevExtTramp(83) -DevExtTramp(84) -DevExtTramp(85) -DevExtTramp(86) -DevExtTramp(87) -DevExtTramp(88) -DevExtTramp(89) -DevExtTramp(90) -DevExtTramp(91) -DevExtTramp(92) -DevExtTramp(93) -DevExtTramp(94) -DevExtTramp(95) -DevExtTramp(96) -DevExtTramp(97) -DevExtTramp(98) -DevExtTramp(99) -DevExtTramp(100) -DevExtTramp(101) -DevExtTramp(102) -DevExtTramp(103) -DevExtTramp(104) -DevExtTramp(105) -DevExtTramp(106) -DevExtTramp(107) -DevExtTramp(108) -DevExtTramp(109) -DevExtTramp(110) -DevExtTramp(111) -DevExtTramp(112) -DevExtTramp(113) -DevExtTramp(114) -DevExtTramp(115) -DevExtTramp(116) -DevExtTramp(117) -DevExtTramp(118) -DevExtTramp(119) -DevExtTramp(120) -DevExtTramp(121) -DevExtTramp(122) -DevExtTramp(123) -DevExtTramp(124) -DevExtTramp(125) -DevExtTramp(126) -DevExtTramp(127) -DevExtTramp(128) -DevExtTramp(129) -DevExtTramp(130) -DevExtTramp(131) -DevExtTramp(132) -DevExtTramp(133) -DevExtTramp(134) -DevExtTramp(135) -DevExtTramp(136) -DevExtTramp(137) -DevExtTramp(138) -DevExtTramp(139) -DevExtTramp(140) -DevExtTramp(141) -DevExtTramp(142) -DevExtTramp(143) -DevExtTramp(144) -DevExtTramp(145) -DevExtTramp(146) -DevExtTramp(147) -DevExtTramp(148) -DevExtTramp(149) -DevExtTramp(150) -DevExtTramp(151) -DevExtTramp(152) -DevExtTramp(153) -DevExtTramp(154) -DevExtTramp(155) -DevExtTramp(156) -DevExtTramp(157) -DevExtTramp(158) -DevExtTramp(159) -DevExtTramp(160) -DevExtTramp(161) -DevExtTramp(162) -DevExtTramp(163) -DevExtTramp(164) -DevExtTramp(165) -DevExtTramp(166) -DevExtTramp(167) -DevExtTramp(168) -DevExtTramp(169) -DevExtTramp(170) -DevExtTramp(171) -DevExtTramp(172) -DevExtTramp(173) -DevExtTramp(174) -DevExtTramp(175) -DevExtTramp(176) -DevExtTramp(177) -DevExtTramp(178) -DevExtTramp(179) -DevExtTramp(180) -DevExtTramp(181) -DevExtTramp(182) -DevExtTramp(183) -DevExtTramp(184) -DevExtTramp(185) -DevExtTramp(186) -DevExtTramp(187) -DevExtTramp(188) -DevExtTramp(189) -DevExtTramp(190) -DevExtTramp(191) -DevExtTramp(192) -DevExtTramp(193) -DevExtTramp(194) -DevExtTramp(195) -DevExtTramp(196) -DevExtTramp(197) -DevExtTramp(198) -DevExtTramp(199) -DevExtTramp(200) -DevExtTramp(201) -DevExtTramp(202) -DevExtTramp(203) -DevExtTramp(204) -DevExtTramp(205) -DevExtTramp(206) -DevExtTramp(207) -DevExtTramp(208) -DevExtTramp(209) -DevExtTramp(210) -DevExtTramp(211) -DevExtTramp(212) -DevExtTramp(213) -DevExtTramp(214) -DevExtTramp(215) -DevExtTramp(216) -DevExtTramp(217) -DevExtTramp(218) -DevExtTramp(219) -DevExtTramp(220) -DevExtTramp(221) -DevExtTramp(222) -DevExtTramp(223) -DevExtTramp(224) -DevExtTramp(225) -DevExtTramp(226) -DevExtTramp(227) -DevExtTramp(228) -DevExtTramp(229) -DevExtTramp(230) -DevExtTramp(231) -DevExtTramp(232) -DevExtTramp(233) -DevExtTramp(234) -DevExtTramp(235) -DevExtTramp(236) -DevExtTramp(237) -DevExtTramp(238) -DevExtTramp(239) -DevExtTramp(240) -DevExtTramp(241) -DevExtTramp(242) -DevExtTramp(243) -DevExtTramp(244) -DevExtTramp(245) -DevExtTramp(246) -DevExtTramp(247) -DevExtTramp(248) -DevExtTramp(249) diff --git a/loader/unknown_ext_chain_gas_aarch64.S b/loader/unknown_ext_chain_gas_aarch.S similarity index 78% rename from loader/unknown_ext_chain_gas_aarch64.S rename to loader/unknown_ext_chain_gas_aarch.S index a4078e4e339dad3752ffec9bbcdc4a955c13c844..a78a32d34c735b0b96cb42d94f6495d444107b37 100644 --- a/loader/unknown_ext_chain_gas_aarch64.S +++ b/loader/unknown_ext_chain_gas_aarch.S @@ -1,5 +1,7 @@ // // Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2024 Valve Corporation +// Copyright (c) 2024 LunarG, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,6 +16,7 @@ // limitations under the License. // // Author: Eric Sullivan +// Author: Charles Giessen // // This code is used to pass on device (including physical device) extensions through the call chain. It must do this without @@ -21,39 +24,74 @@ // VkPhysicalDevice or a dispatchable object it can unwrap the object, possibly overwriting the wrapped physical device, and then // jump to the next function in the call chain -.include "gen_defines.asm" +#include "gen_defines.asm" + +/* + * References: + * - https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros + * - https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst + */ +#if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1 + #define BTI_J hint 36 /* bti j: for jumps, IE br instructions */ + #define BTI_C hint 34 /* bti c: for calls, IE bl instructions */ + #define GNU_PROPERTY_AARCH64_BTI 1 /* bit 0 GNU Notes is for BTI support */ +#else + #define BTI_J + #define BTI_C + #define GNU_PROPERTY_AARCH64_BTI 0 +#endif + +/* + * We just need PAC added to GNU Notes for auditing features, the assembly itself does + * not need pac augmentation at this time because it doesn't make use of the SP aka x30. + */ +#if defined(__ARM_FEATURE_PAC_DEFAULT) + #define GNU_PROPERTY_AARCH64_POINTER_AUTH 2 /* bit 1 GNU Notes is for PAC support */ +#else + #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0 +#endif .if AARCH_64 .macro PhysDevExtTramp num .global vkPhysDevExtTramp\num #if defined(__ELF__) + .type vkPhysDevExtTramp\num, @function .hidden vkPhysDevExtTramp\num #endif .balign 4 +/* + * NOTE: x16 is used for the br register so the pstate.btype is 01 and can + * land on 'bti c' that would be inserted by the compiler in C/C++ functions. + * See: https://developer.arm.com/documentation/102433/0200/Jump-oriented-programming + */ vkPhysDevExtTramp\num: + BTI_C ldr x9, [x0] // Load the loader_instance_dispatch_table* into x9 ldr x0, [x0, PHYS_DEV_OFFSET_PHYS_DEV_TRAMP] // Load the unwrapped VkPhysicalDevice into x0 mov x10, (PHYS_DEV_OFFSET_INST_DISPATCH + (PTR_SIZE * \num)) // Put the offset of the entry in the dispatch table for the function - ldr x11, [x9, x10] // Load the address to branch to out of the dispatch table - br x11 // Branch to the next member of the dispatch chain + ldr x16, [x9, x10] // Load the address to branch to out of the dispatch table + br x16 // Branch to the next member of the dispatch chain .endm .macro PhysDevExtTermin num .global vkPhysDevExtTermin\num #if defined(__ELF__) + .type vkPhysDevExtTermin\num, @function .hidden vkPhysDevExtTermin\num #endif .balign 4 vkPhysDevExtTermin\num: + BTI_C ldr x9, [x0, ICD_TERM_OFFSET_PHYS_DEV_TERM] // Load the loader_icd_term* in x9 mov x11, (DISPATCH_OFFSET_ICD_TERM + (PTR_SIZE * \num)) // Put the offset into the dispatch table in x11 - ldr x10, [x9, x11] // Load the address of the next function in the dispatch chain - cbz x10, terminError\num // Go to the error section if the next function in the chain is NULL + ldr x16, [x9, x11] // Load the address of the next function in the dispatch chain + cbz x16, terminError\num // Go to the error section if the next function in the chain is NULL ldr x0, [x0, PHYS_DEV_OFFSET_PHYS_DEV_TERM] // Unwrap the VkPhysicalDevice in x0 - br x10 // Jump to the next function in the chain + br x16 // Jump to the next function in the chain terminError\num: + BTI_J mov x10, (FUNCTION_OFFSET_INSTANCE + (CHAR_PTR_SIZE * \num)) // Offset of the function name string in the instance ldr x11, [x9, INSTANCE_OFFSET_ICD_TERM] // Load the instance pointer mov x0, x11 // Vulkan instance pointer (first arg) @@ -72,16 +110,92 @@ terminError\num: #endif .balign 4 vkdev_ext\num: + BTI_C ldr x9, [x0] // Load the loader_instance_dispatch_table* into x9 mov x10, (EXT_OFFSET_DEVICE_DISPATCH + (PTR_SIZE * \num)) // Offset of the desired function in the dispatch table - ldr x11, [x9, x10] // Load the function address - br x11 + ldr x16, [x9, x10] // Load the function address + br x16 +.endm + +.else // AARCH_32 + +.macro PhysDevExtTramp num +.global vkPhysDevExtTramp\num +#if defined(__ELF__) + .hidden vkPhysDevExtTramp\num +#endif +.balign 4 + +vkPhysDevExtTrampDispatchEntry\num: .word PHYS_DEV_OFFSET_INST_DISPATCH + (PTR_SIZE * \num) +vkPhysDevExtTramp\num: + ldr r4, [r0] // Load the loader_instance_dispatch_table* into r4 + ldr r0, [r0, #PHYS_DEV_OFFSET_PHYS_DEV_TRAMP] // Load the unwrapped VkPhysicalDevice into r0 + ldr r5, vkPhysDevExtTrampDispatchEntry\num // Put the offset of the entry in the dispatch table for the function + ldr r6, [r4, r5] // Load the address to branch to out of the dispatch table + bx r6 // Branch to the next member of the dispatch chain + +.endm + +.macro PhysDevExtTermin num +.global vkPhysDevExtTermin\num +#if defined(__ELF__) + .hidden vkPhysDevExtTermin\num +#endif +.balign 4 +vkPhysDevExtTerminDispatchEntry\num: .word DISPATCH_OFFSET_ICD_TERM + (PTR_SIZE * \num) +vkPhysDevExtTerminFunctionNameEntry\num: .word FUNCTION_OFFSET_INSTANCE + (CHAR_PTR_SIZE * \num) +vkPhysDevExtTermin\num: + ldr r4, [r0, #ICD_TERM_OFFSET_PHYS_DEV_TERM] // Load the loader_icd_term* in r4 + ldr r6, vkPhysDevExtTerminDispatchEntry\num // Put the offset into the dispatch table in r6 + ldr r5, [r4, r6] // Load the address of the next function in the dispatch chain + cmp r5, #0 + beq terminError\num // Go to the error section if the next function in the chain is NULL + ldr r0, [r0, #PHYS_DEV_OFFSET_PHYS_DEV_TERM] // Unwrap the VkPhysicalDevice in r0 + bx r5 // Jump to the next function in the chain +terminError\num: + ldr r5, vkPhysDevExtTerminFunctionNameEntry\num // Offset of the function name string in the instance + ldr r6, [r4, #INSTANCE_OFFSET_ICD_TERM] // Load the instance pointer + mov r0, r6 // Vulkan instance pointer (first arg) + mov r1, #VULKAN_LOADER_ERROR_BIT // The error logging bit (second arg) + mov r2, #0 // Zero (third arg) + ldr r3, [r6, r5] // The function name (fourth arg) + bl loader_log_asm_function_not_supported // Log the error message before we crash + mov r0, #0 + bx r0 // Crash intentionally by jumping to address zero +.endm + +.macro DevExtTramp num +.global vkdev_ext\num +#if defined(__ELF__) + .hidden vkdev_ext\num +#endif +.balign 4 +vkdev_ext_dispatch_entry\num: .word EXT_OFFSET_DEVICE_DISPATCH + (PTR_SIZE * \num) +vkdev_ext\num: + ldr r4, [r0] // Load the loader_instance_dispatch_table* into r4 + ldr r5, vkdev_ext_dispatch_entry\num // Offset of the desired function in the dispatch table + ldr r6, [r4, r5] // Load the function address + bx r6 .endm .endif #if defined(__ELF__) .section .note.GNU-stack,"",%progbits +/* Add the PAC and BTI support to GNU Notes section for ELF object files */ +#if GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_POINTER_AUTH != 0 + .pushsection .note.gnu.property, "a"; /* Start a new allocatable section */ + .balign 8; /* align it on a byte boundry */ + .long 4; /* size of "GNU\0" */ + .long 0x10; /* size of descriptor */ + .long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU"; + .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4; /* Four bytes of data */ + .long (GNU_PROPERTY_AARCH64_BTI|GNU_PROPERTY_AARCH64_POINTER_AUTH); /* BTI or PAC is enabled */ + .long 0; /* padding for 8 byte alignment */ + .popsection; /* end the section */ +#endif #endif .data diff --git a/loader/unknown_ext_chain_gas_x86.S b/loader/unknown_ext_chain_gas_x86.S index de5a920308275dabb08f7b4c381c3f07e1a4b8fa..5ee02051208a9f465ab174e4b9e4e7aca07f5298 100644 --- a/loader/unknown_ext_chain_gas_x86.S +++ b/loader/unknown_ext_chain_gas_x86.S @@ -33,7 +33,7 @@ #endif .intel_syntax noprefix -.include "gen_defines.asm" +#include "gen_defines.asm" .ifdef X86_64 diff --git a/loader/unknown_ext_chain_marmasm.asm b/loader/unknown_ext_chain_marmasm.asm new file mode 100644 index 0000000000000000000000000000000000000000..f2f352a17d2615072a793b4e49451275da33a982 --- /dev/null +++ b/loader/unknown_ext_chain_marmasm.asm @@ -0,0 +1,896 @@ +; +; Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +; Copyright (c) 2024 Valve Corporation +; Copyright (c) 2024 LunarG, Inc. +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; +; Author: Eric Sullivan +; Author: Charles Giessen +; + +; This code is used to pass on device (including physical device) extensions through the call chain. It must do this without +; creating a stack frame, because the actual parameters of the call are not known. Since the first parameter is known to be a +; VkPhysicalDevice or a dispatchable object it can unwrap the object, possibly overwriting the wrapped physical device, and then +; jump to the next function in the call chain + + + GET gen_defines.asm + + EXTERN loader_log_asm_function_not_supported + + IF AARCH_64==1 + + MACRO + PhysDevExtTramp $num + ALIGN + EXPORT vkPhysDevExtTramp$num [FUNC] +vkPhysDevExtTramp$num FUNCTION + ldr x9, [x0] ; Load the loader_instance_dispatch_table* into x9 + ldr x0, [x0, PHYS_DEV_OFFSET_PHYS_DEV_TRAMP] ; Load the unwrapped VkPhysicalDevice into x0 + mov x10, #(PHYS_DEV_OFFSET_INST_DISPATCH + (PTR_SIZE * $num)) ; Put the offset of the entry in the dispatch table for the function + ldr x11, [x9, x10] ; Load the address to branch to out of the dispatch table + br x11 ; Branch to the next member of the dispatch chain + ENDFUNC + MEND + + MACRO +$label PhysDevExtTermin $num + ALIGN + EXPORT vkPhysDevExtTermin$num [FUNC] +vkPhysDevExtTermin$num FUNCTION + ldr x9, [x0, ICD_TERM_OFFSET_PHYS_DEV_TERM] ; Load the loader_icd_term* in x9 + mov x11, (DISPATCH_OFFSET_ICD_TERM + (PTR_SIZE * $num)) ; Put the offset into the dispatch table in x11 + ldr x10, [x9, x11] ; Load the address of the next function in the dispatch chain + cbz x10, terminError$num ; Go to the error section if the next function in the chain is NULL + ldr x0, [x0, PHYS_DEV_OFFSET_PHYS_DEV_TERM] ; Unwrap the VkPhysicalDevice in x0 + br x10 ; Jump to the next function in the chain +terminError$num + mov x10, (FUNCTION_OFFSET_INSTANCE + (CHAR_PTR_SIZE * $num)) ; Offset of the function name string in the instance + ldr x11, [x9, INSTANCE_OFFSET_ICD_TERM] ; Load the instance pointer + mov x0, x11 ; Vulkan instance pointer (first arg) + mov x1, VULKAN_LOADER_ERROR_BIT ; The error logging bit (second arg) + mov x2, #0 ; Zero (third arg) + ldr x3, [x11, x10] ; The function name (fourth arg) + bl loader_log_asm_function_not_supported ; Log the error message before we crash + mov x0, #0 + br x0 ; Crash intentionally by jumping to address zero + ENDFUNC + MEND + + MACRO + DevExtTramp $num + ALIGN + EXPORT vkdev_ext$num [FUNC] +vkdev_ext$num FUNCTION + ldr x9, [x0] ; Load the loader_instance_dispatch_table* into x9 + mov x10, (EXT_OFFSET_DEVICE_DISPATCH + (PTR_SIZE * $num)) ; Offset of the desired function in the dispatch table + ldr x11, [x9, x10] ; Load the function address + br x11 + ENDFUNC + MEND + +; 32 bit (armhf) assembly + ELSE + + MACRO + PhysDevExtTramp $num + ALIGN + EXPORT vkPhysDevExtTramp$num [FUNC] +vkPhysDevExtTramp$num FUNCTION + ldr r4, [r0] ; Load the loader_instance_dispatch_table* into r4 + ldr r0, [r0, #PHYS_DEV_OFFSET_PHYS_DEV_TRAMP] ; Load the unwrapped VkPhysicalDevice into r0 + mov r5, #(PHYS_DEV_OFFSET_INST_DISPATCH + (PTR_SIZE * $num)) ; Put the offset of the entry in the dispatch table for the function + ldr r6, [r4, r5] ; Load the address to branch to out of the dispatch table + bx r6 ; Branch to the next member of the dispatch chain + ENDFUNC + MEND + + MACRO +$label PhysDevExtTermin $num + ALIGN + EXPORT vkPhysDevExtTermin$num [FUNC] +vkPhysDevExtTermin$num FUNCTION + ldr r4, [r0, #ICD_TERM_OFFSET_PHYS_DEV_TERM] ; Load the loader_icd_term* in r4 + mov r6, #(DISPATCH_OFFSET_ICD_TERM + (PTR_SIZE * $num)) ; Put the offset into the dispatch table in r6 + ldr r5, [r4, r6] ; Load the address of the next function in the dispatch chain + cbz r5, terminError$num ; Go to the error section if the next function in the chain is NULL + ldr r0, [r0, #PHYS_DEV_OFFSET_PHYS_DEV_TERM] ; Unwrap the VkPhysicalDevice in r0 + bx r5 ; Jump to the next function in the chain +terminError$num + mov r5, #(FUNCTION_OFFSET_INSTANCE + (CHAR_PTR_SIZE * $num)) ; Offset of the function name string in the instance + ldr r6, [r4, #INSTANCE_OFFSET_ICD_TERM] ; Load the instance pointer + mov r0, r6 ; Vulkan instance pointer (first arg) + mov r1, #VULKAN_LOADER_ERROR_BIT ; The error logging bit (second arg) + mov r2, #0 ; Zero (third arg) + ldr r3, [r6, r5] ; The function name (fourth arg) + bl loader_log_asm_function_not_supported ; Log the error message before we crash + mov r0, #0 + bx r0 ; Crash intentionally by jumping to address zero + ENDFUNC + MEND + + MACRO + DevExtTramp $num + ALIGN + EXPORT vkdev_ext$num [FUNC] +vkdev_ext$num FUNCTION + ldr r4, [r0] ; Load the loader_instance_dispatch_table* into r4 + mov r5, #(EXT_OFFSET_DEVICE_DISPATCH + (PTR_SIZE * $num)) ; Offset of the desired function in the dispatch table + ldr r6, [r4, r5] ; Load the function address + bx r6 + ENDFUNC + MEND + + ENDIF + + AREA terminator_string_data, DATA, READONLY + +termin_error_string DCB "Function %s not supported for this physical device", 0 + + AREA UnknownFunctionImpl, CODE, READONLY + + PhysDevExtTramp 0 + PhysDevExtTramp 1 + PhysDevExtTramp 2 + PhysDevExtTramp 3 + PhysDevExtTramp 4 + PhysDevExtTramp 5 + PhysDevExtTramp 6 + PhysDevExtTramp 7 + PhysDevExtTramp 8 + PhysDevExtTramp 9 + PhysDevExtTramp 10 + PhysDevExtTramp 11 + PhysDevExtTramp 12 + PhysDevExtTramp 13 + PhysDevExtTramp 14 + PhysDevExtTramp 15 + PhysDevExtTramp 16 + PhysDevExtTramp 17 + PhysDevExtTramp 18 + PhysDevExtTramp 19 + PhysDevExtTramp 20 + PhysDevExtTramp 21 + PhysDevExtTramp 22 + PhysDevExtTramp 23 + PhysDevExtTramp 24 + PhysDevExtTramp 25 + PhysDevExtTramp 26 + PhysDevExtTramp 27 + PhysDevExtTramp 28 + PhysDevExtTramp 29 + PhysDevExtTramp 30 + PhysDevExtTramp 31 + PhysDevExtTramp 32 + PhysDevExtTramp 33 + PhysDevExtTramp 34 + PhysDevExtTramp 35 + PhysDevExtTramp 36 + PhysDevExtTramp 37 + PhysDevExtTramp 38 + PhysDevExtTramp 39 + PhysDevExtTramp 40 + PhysDevExtTramp 41 + PhysDevExtTramp 42 + PhysDevExtTramp 43 + PhysDevExtTramp 44 + PhysDevExtTramp 45 + PhysDevExtTramp 46 + PhysDevExtTramp 47 + PhysDevExtTramp 48 + PhysDevExtTramp 49 + PhysDevExtTramp 50 + PhysDevExtTramp 51 + PhysDevExtTramp 52 + PhysDevExtTramp 53 + PhysDevExtTramp 54 + PhysDevExtTramp 55 + PhysDevExtTramp 56 + PhysDevExtTramp 57 + PhysDevExtTramp 58 + PhysDevExtTramp 59 + PhysDevExtTramp 60 + PhysDevExtTramp 61 + PhysDevExtTramp 62 + PhysDevExtTramp 63 + PhysDevExtTramp 64 + PhysDevExtTramp 65 + PhysDevExtTramp 66 + PhysDevExtTramp 67 + PhysDevExtTramp 68 + PhysDevExtTramp 69 + PhysDevExtTramp 70 + PhysDevExtTramp 71 + PhysDevExtTramp 72 + PhysDevExtTramp 73 + PhysDevExtTramp 74 + PhysDevExtTramp 75 + PhysDevExtTramp 76 + PhysDevExtTramp 77 + PhysDevExtTramp 78 + PhysDevExtTramp 79 + PhysDevExtTramp 80 + PhysDevExtTramp 81 + PhysDevExtTramp 82 + PhysDevExtTramp 83 + PhysDevExtTramp 84 + PhysDevExtTramp 85 + PhysDevExtTramp 86 + PhysDevExtTramp 87 + PhysDevExtTramp 88 + PhysDevExtTramp 89 + PhysDevExtTramp 90 + PhysDevExtTramp 91 + PhysDevExtTramp 92 + PhysDevExtTramp 93 + PhysDevExtTramp 94 + PhysDevExtTramp 95 + PhysDevExtTramp 96 + PhysDevExtTramp 97 + PhysDevExtTramp 98 + PhysDevExtTramp 99 + PhysDevExtTramp 100 + PhysDevExtTramp 101 + PhysDevExtTramp 102 + PhysDevExtTramp 103 + PhysDevExtTramp 104 + PhysDevExtTramp 105 + PhysDevExtTramp 106 + PhysDevExtTramp 107 + PhysDevExtTramp 108 + PhysDevExtTramp 109 + PhysDevExtTramp 110 + PhysDevExtTramp 111 + PhysDevExtTramp 112 + PhysDevExtTramp 113 + PhysDevExtTramp 114 + PhysDevExtTramp 115 + PhysDevExtTramp 116 + PhysDevExtTramp 117 + PhysDevExtTramp 118 + PhysDevExtTramp 119 + PhysDevExtTramp 120 + PhysDevExtTramp 121 + PhysDevExtTramp 122 + PhysDevExtTramp 123 + PhysDevExtTramp 124 + PhysDevExtTramp 125 + PhysDevExtTramp 126 + PhysDevExtTramp 127 + PhysDevExtTramp 128 + PhysDevExtTramp 129 + PhysDevExtTramp 130 + PhysDevExtTramp 131 + PhysDevExtTramp 132 + PhysDevExtTramp 133 + PhysDevExtTramp 134 + PhysDevExtTramp 135 + PhysDevExtTramp 136 + PhysDevExtTramp 137 + PhysDevExtTramp 138 + PhysDevExtTramp 139 + PhysDevExtTramp 140 + PhysDevExtTramp 141 + PhysDevExtTramp 142 + PhysDevExtTramp 143 + PhysDevExtTramp 144 + PhysDevExtTramp 145 + PhysDevExtTramp 146 + PhysDevExtTramp 147 + PhysDevExtTramp 148 + PhysDevExtTramp 149 + PhysDevExtTramp 150 + PhysDevExtTramp 151 + PhysDevExtTramp 152 + PhysDevExtTramp 153 + PhysDevExtTramp 154 + PhysDevExtTramp 155 + PhysDevExtTramp 156 + PhysDevExtTramp 157 + PhysDevExtTramp 158 + PhysDevExtTramp 159 + PhysDevExtTramp 160 + PhysDevExtTramp 161 + PhysDevExtTramp 162 + PhysDevExtTramp 163 + PhysDevExtTramp 164 + PhysDevExtTramp 165 + PhysDevExtTramp 166 + PhysDevExtTramp 167 + PhysDevExtTramp 168 + PhysDevExtTramp 169 + PhysDevExtTramp 170 + PhysDevExtTramp 171 + PhysDevExtTramp 172 + PhysDevExtTramp 173 + PhysDevExtTramp 174 + PhysDevExtTramp 175 + PhysDevExtTramp 176 + PhysDevExtTramp 177 + PhysDevExtTramp 178 + PhysDevExtTramp 179 + PhysDevExtTramp 180 + PhysDevExtTramp 181 + PhysDevExtTramp 182 + PhysDevExtTramp 183 + PhysDevExtTramp 184 + PhysDevExtTramp 185 + PhysDevExtTramp 186 + PhysDevExtTramp 187 + PhysDevExtTramp 188 + PhysDevExtTramp 189 + PhysDevExtTramp 190 + PhysDevExtTramp 191 + PhysDevExtTramp 192 + PhysDevExtTramp 193 + PhysDevExtTramp 194 + PhysDevExtTramp 195 + PhysDevExtTramp 196 + PhysDevExtTramp 197 + PhysDevExtTramp 198 + PhysDevExtTramp 199 + PhysDevExtTramp 200 + PhysDevExtTramp 201 + PhysDevExtTramp 202 + PhysDevExtTramp 203 + PhysDevExtTramp 204 + PhysDevExtTramp 205 + PhysDevExtTramp 206 + PhysDevExtTramp 207 + PhysDevExtTramp 208 + PhysDevExtTramp 209 + PhysDevExtTramp 210 + PhysDevExtTramp 211 + PhysDevExtTramp 212 + PhysDevExtTramp 213 + PhysDevExtTramp 214 + PhysDevExtTramp 215 + PhysDevExtTramp 216 + PhysDevExtTramp 217 + PhysDevExtTramp 218 + PhysDevExtTramp 219 + PhysDevExtTramp 220 + PhysDevExtTramp 221 + PhysDevExtTramp 222 + PhysDevExtTramp 223 + PhysDevExtTramp 224 + PhysDevExtTramp 225 + PhysDevExtTramp 226 + PhysDevExtTramp 227 + PhysDevExtTramp 228 + PhysDevExtTramp 229 + PhysDevExtTramp 230 + PhysDevExtTramp 231 + PhysDevExtTramp 232 + PhysDevExtTramp 233 + PhysDevExtTramp 234 + PhysDevExtTramp 235 + PhysDevExtTramp 236 + PhysDevExtTramp 237 + PhysDevExtTramp 238 + PhysDevExtTramp 239 + PhysDevExtTramp 240 + PhysDevExtTramp 241 + PhysDevExtTramp 242 + PhysDevExtTramp 243 + PhysDevExtTramp 244 + PhysDevExtTramp 245 + PhysDevExtTramp 246 + PhysDevExtTramp 247 + PhysDevExtTramp 248 + PhysDevExtTramp 249 + + PhysDevExtTermin 0 + PhysDevExtTermin 1 + PhysDevExtTermin 2 + PhysDevExtTermin 3 + PhysDevExtTermin 4 + PhysDevExtTermin 5 + PhysDevExtTermin 6 + PhysDevExtTermin 7 + PhysDevExtTermin 8 + PhysDevExtTermin 9 + PhysDevExtTermin 10 + PhysDevExtTermin 11 + PhysDevExtTermin 12 + PhysDevExtTermin 13 + PhysDevExtTermin 14 + PhysDevExtTermin 15 + PhysDevExtTermin 16 + PhysDevExtTermin 17 + PhysDevExtTermin 18 + PhysDevExtTermin 19 + PhysDevExtTermin 20 + PhysDevExtTermin 21 + PhysDevExtTermin 22 + PhysDevExtTermin 23 + PhysDevExtTermin 24 + PhysDevExtTermin 25 + PhysDevExtTermin 26 + PhysDevExtTermin 27 + PhysDevExtTermin 28 + PhysDevExtTermin 29 + PhysDevExtTermin 30 + PhysDevExtTermin 31 + PhysDevExtTermin 32 + PhysDevExtTermin 33 + PhysDevExtTermin 34 + PhysDevExtTermin 35 + PhysDevExtTermin 36 + PhysDevExtTermin 37 + PhysDevExtTermin 38 + PhysDevExtTermin 39 + PhysDevExtTermin 40 + PhysDevExtTermin 41 + PhysDevExtTermin 42 + PhysDevExtTermin 43 + PhysDevExtTermin 44 + PhysDevExtTermin 45 + PhysDevExtTermin 46 + PhysDevExtTermin 47 + PhysDevExtTermin 48 + PhysDevExtTermin 49 + PhysDevExtTermin 50 + PhysDevExtTermin 51 + PhysDevExtTermin 52 + PhysDevExtTermin 53 + PhysDevExtTermin 54 + PhysDevExtTermin 55 + PhysDevExtTermin 56 + PhysDevExtTermin 57 + PhysDevExtTermin 58 + PhysDevExtTermin 59 + PhysDevExtTermin 60 + PhysDevExtTermin 61 + PhysDevExtTermin 62 + PhysDevExtTermin 63 + PhysDevExtTermin 64 + PhysDevExtTermin 65 + PhysDevExtTermin 66 + PhysDevExtTermin 67 + PhysDevExtTermin 68 + PhysDevExtTermin 69 + PhysDevExtTermin 70 + PhysDevExtTermin 71 + PhysDevExtTermin 72 + PhysDevExtTermin 73 + PhysDevExtTermin 74 + PhysDevExtTermin 75 + PhysDevExtTermin 76 + PhysDevExtTermin 77 + PhysDevExtTermin 78 + PhysDevExtTermin 79 + PhysDevExtTermin 80 + PhysDevExtTermin 81 + PhysDevExtTermin 82 + PhysDevExtTermin 83 + PhysDevExtTermin 84 + PhysDevExtTermin 85 + PhysDevExtTermin 86 + PhysDevExtTermin 87 + PhysDevExtTermin 88 + PhysDevExtTermin 89 + PhysDevExtTermin 90 + PhysDevExtTermin 91 + PhysDevExtTermin 92 + PhysDevExtTermin 93 + PhysDevExtTermin 94 + PhysDevExtTermin 95 + PhysDevExtTermin 96 + PhysDevExtTermin 97 + PhysDevExtTermin 98 + PhysDevExtTermin 99 + PhysDevExtTermin 100 + PhysDevExtTermin 101 + PhysDevExtTermin 102 + PhysDevExtTermin 103 + PhysDevExtTermin 104 + PhysDevExtTermin 105 + PhysDevExtTermin 106 + PhysDevExtTermin 107 + PhysDevExtTermin 108 + PhysDevExtTermin 109 + PhysDevExtTermin 110 + PhysDevExtTermin 111 + PhysDevExtTermin 112 + PhysDevExtTermin 113 + PhysDevExtTermin 114 + PhysDevExtTermin 115 + PhysDevExtTermin 116 + PhysDevExtTermin 117 + PhysDevExtTermin 118 + PhysDevExtTermin 119 + PhysDevExtTermin 120 + PhysDevExtTermin 121 + PhysDevExtTermin 122 + PhysDevExtTermin 123 + PhysDevExtTermin 124 + PhysDevExtTermin 125 + PhysDevExtTermin 126 + PhysDevExtTermin 127 + PhysDevExtTermin 128 + PhysDevExtTermin 129 + PhysDevExtTermin 130 + PhysDevExtTermin 131 + PhysDevExtTermin 132 + PhysDevExtTermin 133 + PhysDevExtTermin 134 + PhysDevExtTermin 135 + PhysDevExtTermin 136 + PhysDevExtTermin 137 + PhysDevExtTermin 138 + PhysDevExtTermin 139 + PhysDevExtTermin 140 + PhysDevExtTermin 141 + PhysDevExtTermin 142 + PhysDevExtTermin 143 + PhysDevExtTermin 144 + PhysDevExtTermin 145 + PhysDevExtTermin 146 + PhysDevExtTermin 147 + PhysDevExtTermin 148 + PhysDevExtTermin 149 + PhysDevExtTermin 150 + PhysDevExtTermin 151 + PhysDevExtTermin 152 + PhysDevExtTermin 153 + PhysDevExtTermin 154 + PhysDevExtTermin 155 + PhysDevExtTermin 156 + PhysDevExtTermin 157 + PhysDevExtTermin 158 + PhysDevExtTermin 159 + PhysDevExtTermin 160 + PhysDevExtTermin 161 + PhysDevExtTermin 162 + PhysDevExtTermin 163 + PhysDevExtTermin 164 + PhysDevExtTermin 165 + PhysDevExtTermin 166 + PhysDevExtTermin 167 + PhysDevExtTermin 168 + PhysDevExtTermin 169 + PhysDevExtTermin 170 + PhysDevExtTermin 171 + PhysDevExtTermin 172 + PhysDevExtTermin 173 + PhysDevExtTermin 174 + PhysDevExtTermin 175 + PhysDevExtTermin 176 + PhysDevExtTermin 177 + PhysDevExtTermin 178 + PhysDevExtTermin 179 + PhysDevExtTermin 180 + PhysDevExtTermin 181 + PhysDevExtTermin 182 + PhysDevExtTermin 183 + PhysDevExtTermin 184 + PhysDevExtTermin 185 + PhysDevExtTermin 186 + PhysDevExtTermin 187 + PhysDevExtTermin 188 + PhysDevExtTermin 189 + PhysDevExtTermin 190 + PhysDevExtTermin 191 + PhysDevExtTermin 192 + PhysDevExtTermin 193 + PhysDevExtTermin 194 + PhysDevExtTermin 195 + PhysDevExtTermin 196 + PhysDevExtTermin 197 + PhysDevExtTermin 198 + PhysDevExtTermin 199 + PhysDevExtTermin 200 + PhysDevExtTermin 201 + PhysDevExtTermin 202 + PhysDevExtTermin 203 + PhysDevExtTermin 204 + PhysDevExtTermin 205 + PhysDevExtTermin 206 + PhysDevExtTermin 207 + PhysDevExtTermin 208 + PhysDevExtTermin 209 + PhysDevExtTermin 210 + PhysDevExtTermin 211 + PhysDevExtTermin 212 + PhysDevExtTermin 213 + PhysDevExtTermin 214 + PhysDevExtTermin 215 + PhysDevExtTermin 216 + PhysDevExtTermin 217 + PhysDevExtTermin 218 + PhysDevExtTermin 219 + PhysDevExtTermin 220 + PhysDevExtTermin 221 + PhysDevExtTermin 222 + PhysDevExtTermin 223 + PhysDevExtTermin 224 + PhysDevExtTermin 225 + PhysDevExtTermin 226 + PhysDevExtTermin 227 + PhysDevExtTermin 228 + PhysDevExtTermin 229 + PhysDevExtTermin 230 + PhysDevExtTermin 231 + PhysDevExtTermin 232 + PhysDevExtTermin 233 + PhysDevExtTermin 234 + PhysDevExtTermin 235 + PhysDevExtTermin 236 + PhysDevExtTermin 237 + PhysDevExtTermin 238 + PhysDevExtTermin 239 + PhysDevExtTermin 240 + PhysDevExtTermin 241 + PhysDevExtTermin 242 + PhysDevExtTermin 243 + PhysDevExtTermin 244 + PhysDevExtTermin 245 + PhysDevExtTermin 246 + PhysDevExtTermin 247 + PhysDevExtTermin 248 + PhysDevExtTermin 249 + + DevExtTramp 0 + DevExtTramp 1 + DevExtTramp 2 + DevExtTramp 3 + DevExtTramp 4 + DevExtTramp 5 + DevExtTramp 6 + DevExtTramp 7 + DevExtTramp 8 + DevExtTramp 9 + DevExtTramp 10 + DevExtTramp 11 + DevExtTramp 12 + DevExtTramp 13 + DevExtTramp 14 + DevExtTramp 15 + DevExtTramp 16 + DevExtTramp 17 + DevExtTramp 18 + DevExtTramp 19 + DevExtTramp 20 + DevExtTramp 21 + DevExtTramp 22 + DevExtTramp 23 + DevExtTramp 24 + DevExtTramp 25 + DevExtTramp 26 + DevExtTramp 27 + DevExtTramp 28 + DevExtTramp 29 + DevExtTramp 30 + DevExtTramp 31 + DevExtTramp 32 + DevExtTramp 33 + DevExtTramp 34 + DevExtTramp 35 + DevExtTramp 36 + DevExtTramp 37 + DevExtTramp 38 + DevExtTramp 39 + DevExtTramp 40 + DevExtTramp 41 + DevExtTramp 42 + DevExtTramp 43 + DevExtTramp 44 + DevExtTramp 45 + DevExtTramp 46 + DevExtTramp 47 + DevExtTramp 48 + DevExtTramp 49 + DevExtTramp 50 + DevExtTramp 51 + DevExtTramp 52 + DevExtTramp 53 + DevExtTramp 54 + DevExtTramp 55 + DevExtTramp 56 + DevExtTramp 57 + DevExtTramp 58 + DevExtTramp 59 + DevExtTramp 60 + DevExtTramp 61 + DevExtTramp 62 + DevExtTramp 63 + DevExtTramp 64 + DevExtTramp 65 + DevExtTramp 66 + DevExtTramp 67 + DevExtTramp 68 + DevExtTramp 69 + DevExtTramp 70 + DevExtTramp 71 + DevExtTramp 72 + DevExtTramp 73 + DevExtTramp 74 + DevExtTramp 75 + DevExtTramp 76 + DevExtTramp 77 + DevExtTramp 78 + DevExtTramp 79 + DevExtTramp 80 + DevExtTramp 81 + DevExtTramp 82 + DevExtTramp 83 + DevExtTramp 84 + DevExtTramp 85 + DevExtTramp 86 + DevExtTramp 87 + DevExtTramp 88 + DevExtTramp 89 + DevExtTramp 90 + DevExtTramp 91 + DevExtTramp 92 + DevExtTramp 93 + DevExtTramp 94 + DevExtTramp 95 + DevExtTramp 96 + DevExtTramp 97 + DevExtTramp 98 + DevExtTramp 99 + DevExtTramp 100 + DevExtTramp 101 + DevExtTramp 102 + DevExtTramp 103 + DevExtTramp 104 + DevExtTramp 105 + DevExtTramp 106 + DevExtTramp 107 + DevExtTramp 108 + DevExtTramp 109 + DevExtTramp 110 + DevExtTramp 111 + DevExtTramp 112 + DevExtTramp 113 + DevExtTramp 114 + DevExtTramp 115 + DevExtTramp 116 + DevExtTramp 117 + DevExtTramp 118 + DevExtTramp 119 + DevExtTramp 120 + DevExtTramp 121 + DevExtTramp 122 + DevExtTramp 123 + DevExtTramp 124 + DevExtTramp 125 + DevExtTramp 126 + DevExtTramp 127 + DevExtTramp 128 + DevExtTramp 129 + DevExtTramp 130 + DevExtTramp 131 + DevExtTramp 132 + DevExtTramp 133 + DevExtTramp 134 + DevExtTramp 135 + DevExtTramp 136 + DevExtTramp 137 + DevExtTramp 138 + DevExtTramp 139 + DevExtTramp 140 + DevExtTramp 141 + DevExtTramp 142 + DevExtTramp 143 + DevExtTramp 144 + DevExtTramp 145 + DevExtTramp 146 + DevExtTramp 147 + DevExtTramp 148 + DevExtTramp 149 + DevExtTramp 150 + DevExtTramp 151 + DevExtTramp 152 + DevExtTramp 153 + DevExtTramp 154 + DevExtTramp 155 + DevExtTramp 156 + DevExtTramp 157 + DevExtTramp 158 + DevExtTramp 159 + DevExtTramp 160 + DevExtTramp 161 + DevExtTramp 162 + DevExtTramp 163 + DevExtTramp 164 + DevExtTramp 165 + DevExtTramp 166 + DevExtTramp 167 + DevExtTramp 168 + DevExtTramp 169 + DevExtTramp 170 + DevExtTramp 171 + DevExtTramp 172 + DevExtTramp 173 + DevExtTramp 174 + DevExtTramp 175 + DevExtTramp 176 + DevExtTramp 177 + DevExtTramp 178 + DevExtTramp 179 + DevExtTramp 180 + DevExtTramp 181 + DevExtTramp 182 + DevExtTramp 183 + DevExtTramp 184 + DevExtTramp 185 + DevExtTramp 186 + DevExtTramp 187 + DevExtTramp 188 + DevExtTramp 189 + DevExtTramp 190 + DevExtTramp 191 + DevExtTramp 192 + DevExtTramp 193 + DevExtTramp 194 + DevExtTramp 195 + DevExtTramp 196 + DevExtTramp 197 + DevExtTramp 198 + DevExtTramp 199 + DevExtTramp 200 + DevExtTramp 201 + DevExtTramp 202 + DevExtTramp 203 + DevExtTramp 204 + DevExtTramp 205 + DevExtTramp 206 + DevExtTramp 207 + DevExtTramp 208 + DevExtTramp 209 + DevExtTramp 210 + DevExtTramp 211 + DevExtTramp 212 + DevExtTramp 213 + DevExtTramp 214 + DevExtTramp 215 + DevExtTramp 216 + DevExtTramp 217 + DevExtTramp 218 + DevExtTramp 219 + DevExtTramp 220 + DevExtTramp 221 + DevExtTramp 222 + DevExtTramp 223 + DevExtTramp 224 + DevExtTramp 225 + DevExtTramp 226 + DevExtTramp 227 + DevExtTramp 228 + DevExtTramp 229 + DevExtTramp 230 + DevExtTramp 231 + DevExtTramp 232 + DevExtTramp 233 + DevExtTramp 234 + DevExtTramp 235 + DevExtTramp 236 + DevExtTramp 237 + DevExtTramp 238 + DevExtTramp 239 + DevExtTramp 240 + DevExtTramp 241 + DevExtTramp 242 + DevExtTramp 243 + DevExtTramp 244 + DevExtTramp 245 + DevExtTramp 246 + DevExtTramp 247 + DevExtTramp 248 + DevExtTramp 249 + + END diff --git a/loader/unknown_function_handling.c b/loader/unknown_function_handling.c index d6ebe7f87a8b2176cfafacd5ec06bdd27598b322..2ff4be1d6408c17a8bc21364296eeebafd7f8b83 100644 --- a/loader/unknown_function_handling.c +++ b/loader/unknown_function_handling.c @@ -24,6 +24,44 @@ #include "unknown_function_handling.h" +// If the assembly code necessary for unknown functions isn't supported, then replace all of the functions with stubs. +// This way, if an application queries for an unknown function, they receive NULL and can act accordingly. +// Previously, there was a fallback path written in C. However, it depended on the compiler optimizing the functions +// in such a way as to not disturb the callstack. This reliance on implementation defined behavior is unsustainable and was only +// known to work with GCC. +#if !defined(UNKNOWN_FUNCTIONS_SUPPORTED) + +void loader_init_dispatch_dev_ext(struct loader_instance *inst, struct loader_device *dev) { + (void)inst; + (void)dev; +} +void *loader_dev_ext_gpa_tramp(struct loader_instance *inst, const char *funcName) { + (void)inst; + (void)funcName; + return NULL; +} +void *loader_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) { + (void)inst; + (void)funcName; + return NULL; +} + +void *loader_phys_dev_ext_gpa_tramp(struct loader_instance *inst, const char *funcName) { + (void)inst; + (void)funcName; + return NULL; +} +void *loader_phys_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) { + (void)inst; + (void)funcName; + return NULL; +} + +void loader_free_dev_ext_table(struct loader_instance *inst) { (void)inst; } +void loader_free_phys_dev_ext_table(struct loader_instance *inst) { (void)inst; } + +#else + #include "allocation.h" #include "log.h" @@ -337,3 +375,5 @@ void *loader_phys_dev_ext_gpa_tramp(struct loader_instance *inst, const char *fu void *loader_phys_dev_ext_gpa_term(struct loader_instance *inst, const char *funcName) { return loader_phys_dev_ext_gpa_impl(inst, funcName, false); } + +#endif diff --git a/loader/vk_loader_platform.h b/loader/vk_loader_platform.h index 1f804744d514ec6c2270e047c936bf8152519cd9..9a3b9c1c97734da69fbd28364ab749c4380a0237 100644 --- a/loader/vk_loader_platform.h +++ b/loader/vk_loader_platform.h @@ -71,9 +71,9 @@ #include #include #include -#endif // defined(_WIN32) #include "stack_allocation.h" +#endif // defined(_WIN32) #if defined(APPLE_STATIC_LOADER) && !defined(__APPLE__) #error "APPLE_STATIC_LOADER can only be defined on Apple platforms!" @@ -89,6 +89,17 @@ #define LOADER_EXPORT #endif +// For testing purposes, we want to expose some functions not normally callable on the library +#if defined(SHOULD_EXPORT_TEST_FUNCTIONS) +#if defined(_WIN32) +#define TEST_FUNCTION_EXPORT __declspec(dllexport) +#else +#define TEST_FUNCTION_EXPORT LOADER_EXPORT +#endif +#else +#define TEST_FUNCTION_EXPORT +#endif + #define MAX_STRING_SIZE 1024 // This is defined in vk_layer.h, but if there's problems we need to create the define @@ -100,10 +111,10 @@ // Environment Variable information #define VK_ICD_FILENAMES_ENV_VAR "VK_ICD_FILENAMES" // Deprecated in v1.3.207 loader #define VK_DRIVER_FILES_ENV_VAR "VK_DRIVER_FILES" -#define VK_LAYER_PATH_ENV_VAR "VK_LAYER_PATH" +#define VK_EXPLICIT_LAYER_PATH_ENV_VAR "VK_LAYER_PATH" // Support added in v1.3.207 loader #define VK_ADDITIONAL_DRIVER_FILES_ENV_VAR "VK_ADD_DRIVER_FILES" -#define VK_ADDITIONAL_LAYER_PATH_ENV_VAR "VK_ADD_LAYER_PATH" +#define VK_ADDITIONAL_EXPLICIT_LAYER_PATH_ENV_VAR "VK_ADD_LAYER_PATH" // Support added in v1.3.234 loader #define VK_LAYERS_ENABLE_ENV_VAR "VK_LOADER_LAYERS_ENABLE" #define VK_LAYERS_DISABLE_ENV_VAR "VK_LOADER_LAYERS_DISABLE" @@ -115,6 +126,9 @@ #define VK_LOADER_DISABLE_ALL_LAYERS_VAR_3 "**" #define VK_LOADER_DISABLE_IMPLICIT_LAYERS_VAR "~implicit~" #define VK_LOADER_DISABLE_EXPLICIT_LAYERS_VAR "~explicit~" +// Support added in v1.3.295 loader +#define VK_IMPLICIT_LAYER_PATH_ENV_VAR "VK_IMPLICIT_LAYER_PATH" +#define VK_ADDITIONAL_IMPLICIT_LAYER_PATH_ENV_VAR "VK_ADD_IMPLICIT_LAYER_PATH" // Override layer information #define VK_OVERRIDE_LAYER_NAME "VK_LAYER_LUNARG_override" @@ -151,8 +165,10 @@ #define VK_SETTINGS_INFO_REGISTRY_LOC "" #if defined(__QNX__) +#ifndef SYSCONFDIR #define SYSCONFDIR "/etc" #endif +#endif // C99: #define PRINTF_SIZE_T_SPECIFIER "%zu" @@ -227,9 +243,14 @@ extern bool loader_disable_dynamic_library_unloading; // Returns true if the DIRECTORY_SYMBOL is contained within path static inline bool loader_platform_is_path(const char *path) { return strchr(path, DIRECTORY_SYMBOL) != NULL; } -// The once init functionality is not used when building a DLL on Windows. This is because there is no way to clean up the -// resources allocated by anything allocated by once init. This isn't a problem for static libraries, but it is for dynamic -// ones. When building a DLL, we use DllMain() instead to allow properly cleaning up resources. +// The loader has various initialization tasks which it must do before user code can run. This includes initializing synchronization +// objects, determining the log level, writing the version of the loader to the log, and loading dll's (on windows). On linux, the +// solution is simply running the initialization code in __attribute__((constructor)), which MacOS uses when the loader is +// dynamically linked. When statically linking on MacOS, the setup code instead uses pthread_once to run the logic a single time +// regardless of which API function the application calls first. On Windows, the equivalent way to run code at dll load time is +// DllMain which has many limitations placed upon it. Instead, the Windows code follows MacOS and does initialization in the first +// API call made, using InitOnceExecuteOnce, except for initialization primitives which must be done in DllMain. This is because +// there is no way to clean up the resources allocated by anything allocated by once init. #if defined(APPLE_STATIC_LOADER) static inline void loader_platform_thread_once_fn(pthread_once_t *ctl, void (*func)(void)) { @@ -240,6 +261,13 @@ static inline void loader_platform_thread_once_fn(pthread_once_t *ctl, void (*fu #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) pthread_once_t var = PTHREAD_ONCE_INIT; #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var) extern pthread_once_t var; #define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_once_fn(ctl, func); +#elif defined(WIN32) +static inline void loader_platform_thread_win32_once_fn(INIT_ONCE *ctl, PINIT_ONCE_FN func) { + InitOnceExecuteOnce(ctl, func, NULL, NULL); +} +#define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) INIT_ONCE var = INIT_ONCE_STATIC_INIT; +#define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var) extern INIT_ONCE var; +#define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_win32_once_fn(ctl, func); #else #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) #define LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(var) @@ -329,7 +357,9 @@ static inline char *loader_platform_executable_path(char *buffer, size_t size) { static inline char *loader_platform_executable_path(char *buffer, size_t size) { return NULL; } #elif defined(__QNX__) +#ifndef SYSCONFDIR #define SYSCONFDIR "/etc" +#endif #include #include diff --git a/loader/vulkan-1.def b/loader/vulkan-1.def index f1aab78805aec59ed933803afd67af232e69cba0..de598bc07583165e2c5c479e42e555c237e6ace7 100644 --- a/loader/vulkan-1.def +++ b/loader/vulkan-1.def @@ -1,9 +1,9 @@ ;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; -; Copyright (c) 2015-2021 The Khronos Group Inc. -; Copyright (c) 2015-2021 Valve Corporation -; Copyright (c) 2015-2021 LunarG, Inc. +; Copyright (c) 2015-2024 The Khronos Group Inc. +; Copyright (c) 2015-2024 Valve Corporation +; Copyright (c) 2015-2024 LunarG, Inc. ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. @@ -275,4 +275,24 @@ EXPORTS vkCmdSetPrimitiveRestartEnable vkGetDeviceBufferMemoryRequirements vkGetDeviceImageMemoryRequirements - vkGetDeviceImageSparseMemoryRequirements \ No newline at end of file + vkGetDeviceImageSparseMemoryRequirements + + vkCmdSetLineStipple + vkMapMemory2 + vkUnmapMemory2 + vkCmdBindIndexBuffer2 + vkGetRenderingAreaGranularity + vkGetDeviceImageSubresourceLayout + vkGetImageSubresourceLayout2 + vkCmdPushDescriptorSet + vkCmdPushDescriptorSetWithTemplate + vkCmdSetRenderingAttachmentLocations + vkCmdSetRenderingInputAttachmentIndices + vkCmdBindDescriptorSets2 + vkCmdPushConstants2 + vkCmdPushDescriptorSet2 + vkCmdPushDescriptorSetWithTemplate2 + vkCopyMemoryToImage + vkCopyImageToMemory + vkCopyImageToImage + vkTransitionImageLayout diff --git a/loader/wsi.c b/loader/wsi.c index 56f9e6e1f1866825c298837a10559275adcde651..000fe1219caed287977a4a96a2e9072eadde6b43 100644 --- a/loader/wsi.c +++ b/loader/wsi.c @@ -31,6 +31,7 @@ #include "allocation.h" #include "loader.h" #include "log.h" +#include "stack_allocation.h" #include "vk_loader_platform.h" #include "wsi.h" @@ -186,8 +187,6 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR(VkInstance instance loader_inst->disp->layer_inst_disp.DestroySurfaceKHR(loader_inst->instance, surface, pAllocator); } -// TODO probably need to lock around all the loader_get_instance() calls. - // This is the instance chain terminator function for DestroySurfaceKHR VKAPI_ATTR void VKAPI_CALL terminator_DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator) { @@ -195,25 +194,21 @@ VKAPI_ATTR void VKAPI_CALL terminator_DestroySurfaceKHR(VkInstance instance, VkS VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(surface); if (NULL != icd_surface) { - if (NULL != icd_surface->real_icd_surfaces) { - uint32_t i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { - if (NULL != icd_term->dispatch.DestroySurfaceKHR && - (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[i]) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, icd_surface->real_icd_surfaces[i], pAllocator); - icd_surface->real_icd_surfaces[i] = (VkSurfaceKHR)(uintptr_t)NULL; - } - } else { - // The real_icd_surface for any ICD not supporting the - // proper interface version should be NULL. If not, then - // we have a problem. - assert((VkSurfaceKHR)(uintptr_t)NULL == icd_surface->real_icd_surfaces[i]); + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { + if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { + if (NULL != icd_term->dispatch.DestroySurfaceKHR && icd_term->surface_list.list[icd_surface->surface_index]) { + icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, + icd_term->surface_list.list[icd_surface->surface_index], pAllocator); + icd_term->surface_list.list[icd_surface->surface_index] = (VkSurfaceKHR)(uintptr_t)NULL; } + } else { + // The real_icd_surface for any ICD not supporting the + // proper interface version should be NULL. If not, then + // we have a problem. + assert((VkSurfaceKHR)(uintptr_t)NULL == icd_term->surface_list.list[icd_surface->surface_index]); } - loader_instance_heap_free(loader_inst, icd_surface->real_icd_surfaces); } - + loader_release_object_from_list(&loader_inst->surfaces_list, icd_surface->surface_index); loader_instance_heap_free(loader_inst, (void *)(uintptr_t)surface); } } @@ -265,10 +260,11 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceSupportKHR(VkP } VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; - if (NULL != icd_surface->real_icd_surfaces && - (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[phys_dev_term->icd_index]) { + if (NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { return icd_term->dispatch.GetPhysicalDeviceSurfaceSupportKHR( - phys_dev_term->phys_dev, queueFamilyIndex, icd_surface->real_icd_surfaces[phys_dev_term->icd_index], pSupported); + phys_dev_term->phys_dev, queueFamilyIndex, icd_term->surface_list.list[icd_surface->surface_index], pSupported); } return icd_term->dispatch.GetPhysicalDeviceSurfaceSupportKHR(phys_dev_term->phys_dev, queueFamilyIndex, surface, pSupported); @@ -319,10 +315,12 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilitiesKH } VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; - if (NULL != icd_surface->real_icd_surfaces && - (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[phys_dev_term->icd_index]) { + if (NULL != phys_dev_term->this_icd_term->surface_list.list && + phys_dev_term->this_icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index]) { return icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR( - phys_dev_term->phys_dev, icd_surface->real_icd_surfaces[phys_dev_term->icd_index], pSurfaceCapabilities); + phys_dev_term->phys_dev, phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index], + pSurfaceCapabilities); } return icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR(phys_dev_term->phys_dev, surface, pSurfaceCapabilities); @@ -370,18 +368,20 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceFormatsKHR(VkP // Zero out the format count as this driver doesn't support WSI functionality *pSurfaceFormatCount = 0; loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, - "ICD for selected physical device does not export vkGetPhysicalDeviceSurfaceCapabilitiesKHR!"); + "ICD for selected physical device does not export vkGetPhysicalDeviceSurfaceFormatsKHR!"); return VK_SUCCESS; } - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; - if (NULL != icd_surface->real_icd_surfaces && - (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[phys_dev_term->icd_index]) { - return icd_term->dispatch.GetPhysicalDeviceSurfaceFormatsKHR(phys_dev_term->phys_dev, - icd_surface->real_icd_surfaces[phys_dev_term->icd_index], - pSurfaceFormatCount, pSurfaceFormats); + if (VK_NULL_HANDLE != surface) { + VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; + if (NULL != phys_dev_term->this_icd_term->surface_list.list && + phys_dev_term->this_icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index]) { + return icd_term->dispatch.GetPhysicalDeviceSurfaceFormatsKHR( + phys_dev_term->phys_dev, phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index], + pSurfaceFormatCount, pSurfaceFormats); + } } - return icd_term->dispatch.GetPhysicalDeviceSurfaceFormatsKHR(phys_dev_term->phys_dev, surface, pSurfaceFormatCount, pSurfaceFormats); } @@ -431,14 +431,17 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfacePresentModesKH "ICD for selected physical device does not export vkGetPhysicalDeviceSurfacePresentModesKHR!"); return VK_SUCCESS; } + if (VK_NULL_HANDLE != surface) { + VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; - if (NULL != icd_surface->real_icd_surfaces && - (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[phys_dev_term->icd_index]) { - return icd_term->dispatch.GetPhysicalDeviceSurfacePresentModesKHR( - phys_dev_term->phys_dev, icd_surface->real_icd_surfaces[phys_dev_term->icd_index], pPresentModeCount, pPresentModes); + if (icd_surface != NULL && NULL != phys_dev_term->this_icd_term->surface_list.list && + phys_dev_term->this_icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index]) { + return icd_term->dispatch.GetPhysicalDeviceSurfacePresentModesKHR( + phys_dev_term->phys_dev, phys_dev_term->this_icd_term->surface_list.list[icd_surface->surface_index], + pPresentModeCount, pPresentModes); + } } - return icd_term->dispatch.GetPhysicalDeviceSurfacePresentModesKHR(phys_dev_term->phys_dev, surface, pPresentModeCount, pPresentModes); } @@ -468,9 +471,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR(VkDevice devic VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "vkCreateSwapchainKHR Terminator: device handle. This is likely the result of a " @@ -491,20 +493,19 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateSwapchainKHR(VkDevice device, co return VK_SUCCESS; } VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pCreateInfo->surface; - if (NULL != icd_surface->real_icd_surfaces) { - if ((VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) { - // We found the ICD, and there is an ICD KHR surface - // associated with it, so copy the CreateInfo struct - // and point it at the ICD's surface. - VkSwapchainCreateInfoKHR *pCreateCopy = loader_stack_alloc(sizeof(VkSwapchainCreateInfoKHR)); - if (NULL == pCreateCopy) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - memcpy(pCreateCopy, pCreateInfo, sizeof(VkSwapchainCreateInfoKHR)); - pCreateCopy->surface = icd_surface->real_icd_surfaces[icd_index]; - return dev->loader_dispatch.extension_terminator_dispatch.CreateSwapchainKHR(device, pCreateCopy, pAllocator, - pSwapchain); + if (NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { + // We found the ICD, and there is an ICD KHR surface + // associated with it, so copy the CreateInfo struct + // and point it at the ICD's surface. + VkSwapchainCreateInfoKHR *pCreateCopy = loader_stack_alloc(sizeof(VkSwapchainCreateInfoKHR)); + if (NULL == pCreateCopy) { + return VK_ERROR_OUT_OF_HOST_MEMORY; } + memcpy(pCreateCopy, pCreateInfo, sizeof(VkSwapchainCreateInfoKHR)); + pCreateCopy->surface = icd_term->surface_list.list[icd_surface->surface_index]; + return dev->loader_dispatch.extension_terminator_dispatch.CreateSwapchainKHR(device, pCreateCopy, pAllocator, pSwapchain); } return dev->loader_dispatch.extension_terminator_dispatch.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); } @@ -556,25 +557,75 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR(VkQueue queue, co return disp->QueuePresentKHR(queue, pPresentInfo); } -VkIcdSurface *AllocateIcdSurfaceStruct(struct loader_instance *instance, size_t base_size, size_t platform_size) { +VkResult allocate_icd_surface_struct(struct loader_instance *instance, size_t base_size, size_t platform_size, + const VkAllocationCallbacks *pAllocator, VkIcdSurface **out_icd_surface) { + uint32_t next_index = 0; + VkIcdSurface *icd_surface = NULL; + VkResult res = loader_get_next_available_entry(instance, &instance->surfaces_list, &next_index, pAllocator); + if (res != VK_SUCCESS) { + goto out; + } + // Next, if so, proceed with the implementation of this function: - VkIcdSurface *pIcdSurface = loader_instance_heap_alloc(instance, sizeof(VkIcdSurface), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); - if (pIcdSurface != NULL) { - // Setup the new sizes and offsets so we can grow the structures in the - // future without having problems - pIcdSurface->base_size = (uint32_t)base_size; - pIcdSurface->platform_size = (uint32_t)platform_size; - pIcdSurface->non_platform_offset = (uint32_t)((uint8_t *)(&pIcdSurface->base_size) - (uint8_t *)pIcdSurface); - pIcdSurface->entire_size = sizeof(VkIcdSurface); - - pIcdSurface->real_icd_surfaces = loader_instance_heap_calloc(instance, sizeof(VkSurfaceKHR) * instance->total_icd_count, - VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); - if (pIcdSurface->real_icd_surfaces == NULL) { - loader_instance_heap_free(instance, pIcdSurface); - pIcdSurface = NULL; + icd_surface = loader_instance_heap_alloc(instance, sizeof(VkIcdSurface), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (icd_surface == NULL) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + // Setup the new sizes and offsets so we can grow the structures in the + // future without having problems + icd_surface->base_size = (uint32_t)base_size; + icd_surface->platform_size = (uint32_t)platform_size; + icd_surface->non_platform_offset = (uint32_t)((uint8_t *)(&icd_surface->base_size) - (uint8_t *)icd_surface); + icd_surface->entire_size = sizeof(VkIcdSurface); + icd_surface->surface_index = next_index; + + for (struct loader_icd_term *icd_term = instance->icd_terms; icd_term != NULL; icd_term = icd_term->next) { + if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { + if (icd_term->surface_list.list == NULL) { + res = + loader_init_generic_list(instance, (struct loader_generic_list *)&icd_term->surface_list, sizeof(VkSurfaceKHR)); + if (res != VK_SUCCESS) { + goto out; + } + } else if (icd_term->surface_list.capacity <= next_index * sizeof(VkSurfaceKHR)) { + res = loader_resize_generic_list(instance, (struct loader_generic_list *)&icd_term->surface_list); + if (res != VK_SUCCESS) { + goto out; + } + } } } - return pIcdSurface; + + *out_icd_surface = icd_surface; +out: + if (res != VK_SUCCESS) { + loader_instance_heap_free(instance, icd_surface); + // cleanup of icd_term->surface_list is done during instance destruction + } + return res; +} + +void cleanup_surface_creation(struct loader_instance *loader_inst, VkResult result, VkIcdSurface *icd_surface, + const VkAllocationCallbacks *pAllocator) { + if (VK_SUCCESS != result && NULL != icd_surface) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { + if (NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index] && NULL != icd_term->dispatch.DestroySurfaceKHR) { + icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, icd_term->surface_list.list[icd_surface->surface_index], + pAllocator); + } + } + if (loader_inst->surfaces_list.list && + loader_inst->surfaces_list.capacity > icd_surface->surface_index * sizeof(struct loader_used_object_status)) { + loader_inst->surfaces_list.list[icd_surface->surface_index].status = VK_FALSE; + if (NULL != pAllocator) { + loader_inst->surfaces_list.list[icd_surface->surface_index].allocation_callbacks = *pAllocator; + } + } + loader_instance_heap_free(loader_inst, icd_surface); + } } #if defined(VK_USE_PLATFORM_WIN32_KHR) @@ -598,9 +649,9 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR(VkInstance // This is the instance chain terminator function for CreateWin32SurfaceKHR VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateWin32SurfaceKHR(VkInstance instance, const VkWin32SurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // Initialize pSurface to NULL just to be safe. *pSurface = VK_NULL_HANDLE; @@ -609,53 +660,40 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateWin32SurfaceKHR(VkInstance insta if (!loader_inst->wsi_win32_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_KHR_win32_surface extension not enabled. vkCreateWin32SurfaceKHR not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->win_surf.base), sizeof(pIcdSurface->win_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->win_surf.base), sizeof(icd_surface->win_surf), pAllocator, + &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->win_surf.base.platform = VK_ICD_WSI_PLATFORM_WIN32; - pIcdSurface->win_surf.hinstance = pCreateInfo->hinstance; - pIcdSurface->win_surf.hwnd = pCreateInfo->hwnd; + icd_surface->win_surf.base.platform = VK_ICD_WSI_PLATFORM_WIN32; + icd_surface->win_surf.hinstance = pCreateInfo->hinstance; + icd_surface->win_surf.hwnd = pCreateInfo->hwnd; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateWin32SurfaceKHR) { - vkRes = icd_term->dispatch.CreateWin32SurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateWin32SurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: - - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); + return result; } // This is the trampoline entrypoint for @@ -719,62 +757,50 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateWaylandSurfaceKHR(VkInstanc VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateWaylandSurfaceKHR(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_wayland_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_KHR_wayland_surface extension not enabled. vkCreateWaylandSurfaceKHR not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->wayland_surf.base), sizeof(pIcdSurface->wayland_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->wayland_surf.base), sizeof(icd_surface->wayland_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->wayland_surf.base.platform = VK_ICD_WSI_PLATFORM_WAYLAND; - pIcdSurface->wayland_surf.display = pCreateInfo->display; - pIcdSurface->wayland_surf.surface = pCreateInfo->surface; + icd_surface->wayland_surf.base.platform = VK_ICD_WSI_PLATFORM_WAYLAND; + icd_surface->wayland_surf.display = pCreateInfo->display; + icd_surface->wayland_surf.surface = pCreateInfo->surface; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateWaylandSurfaceKHR) { - vkRes = icd_term->dispatch.CreateWaylandSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateWaylandSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } // This is the trampoline entrypoint for @@ -841,62 +867,50 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR(VkInstance in // This is the instance chain terminator function for CreateXcbSurfaceKHR VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateXcbSurfaceKHR(VkInstance instance, const VkXcbSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_xcb_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_KHR_xcb_surface extension not enabled. vkCreateXcbSurfaceKHR not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->xcb_surf.base), sizeof(pIcdSurface->xcb_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->xcb_surf.base), sizeof(icd_surface->xcb_surf), pAllocator, + &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->xcb_surf.base.platform = VK_ICD_WSI_PLATFORM_XCB; - pIcdSurface->xcb_surf.connection = pCreateInfo->connection; - pIcdSurface->xcb_surf.window = pCreateInfo->window; + icd_surface->xcb_surf.base.platform = VK_ICD_WSI_PLATFORM_XCB; + icd_surface->xcb_surf.connection = pCreateInfo->connection; + icd_surface->xcb_surf.window = pCreateInfo->window; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateXcbSurfaceKHR) { - vkRes = icd_term->dispatch.CreateXcbSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateXcbSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } // This is the trampoline entrypoint for @@ -966,62 +980,50 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR(VkInstance i // This is the instance chain terminator function for CreateXlibSurfaceKHR VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateXlibSurfaceKHR(VkInstance instance, const VkXlibSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_xlib_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_KHR_xlib_surface extension not enabled. vkCreateXlibSurfaceKHR not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->xlib_surf.base), sizeof(pIcdSurface->xlib_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->xlib_surf.base), sizeof(icd_surface->xlib_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->xlib_surf.base.platform = VK_ICD_WSI_PLATFORM_XLIB; - pIcdSurface->xlib_surf.dpy = pCreateInfo->dpy; - pIcdSurface->xlib_surf.window = pCreateInfo->window; + icd_surface->xlib_surf.base.platform = VK_ICD_WSI_PLATFORM_XLIB; + icd_surface->xlib_surf.dpy = pCreateInfo->dpy; + icd_surface->xlib_surf.window = pCreateInfo->window; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateXlibSurfaceKHR) { - vkRes = icd_term->dispatch.CreateXlibSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateXlibSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } // This is the trampoline entrypoint for @@ -1090,63 +1092,50 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDirectFBSurfaceEXT(VkInstance in const VkDirectFBSurfaceCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_directfb_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_EXT_directfb_surface extension not enabled. vkCreateDirectFBSurfaceEXT not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = - AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->directfb_surf.base), sizeof(pIcdSurface->directfb_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->directfb_surf.base), sizeof(icd_surface->directfb_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->directfb_surf.base.platform = VK_ICD_WSI_PLATFORM_DIRECTFB; - pIcdSurface->directfb_surf.dfb = pCreateInfo->dfb; - pIcdSurface->directfb_surf.surface = pCreateInfo->surface; + icd_surface->directfb_surf.base.platform = VK_ICD_WSI_PLATFORM_DIRECTFB; + icd_surface->directfb_surf.dfb = pCreateInfo->dfb; + icd_surface->directfb_surf.surface = pCreateInfo->surface; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateDirectFBSurfaceEXT) { - vkRes = icd_term->dispatch.CreateDirectFBSurfaceEXT(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateDirectFBSurfaceEXT(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } // This is the trampoline entrypoint for @@ -1225,16 +1214,16 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateAndroidSurfaceKHR(VkInstance ins } // Next, if so, proceed with the implementation of this function: - VkIcdSurfaceAndroid *pIcdSurface = + VkIcdSurfaceAndroid *icd_surface = loader_instance_heap_alloc(loader_inst, sizeof(VkIcdSurfaceAndroid), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); - if (pIcdSurface == NULL) { + if (icd_surface == NULL) { return VK_ERROR_OUT_OF_HOST_MEMORY; } - pIcdSurface->base.platform = VK_ICD_WSI_PLATFORM_ANDROID; - pIcdSurface->window = pCreateInfo->window; + icd_surface->base.platform = VK_ICD_WSI_PLATFORM_ANDROID; + icd_surface->window = pCreateInfo->window; - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; return VK_SUCCESS; } @@ -1309,58 +1298,47 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateHeadlessSurfaceEXT(VkInstance in const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - struct loader_instance *inst = loader_get_instance(instance); - VkIcdSurface *pIcdSurface = NULL; - VkResult vkRes = VK_SUCCESS; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); - if (!inst->wsi_headless_surface_enabled) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, + // First, check to ensure the appropriate extension was enabled: + struct loader_instance *loader_inst = loader_get_instance(instance); + if (!loader_inst->wsi_headless_surface_enabled) { + loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_EXT_headless_surface extension not enabled. " "vkCreateHeadlessSurfaceEXT not executed!"); return VK_SUCCESS; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(inst, sizeof(pIcdSurface->headless_surf.base), sizeof(pIcdSurface->headless_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->headless_surf.base), sizeof(icd_surface->headless_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->headless_surf.base.platform = VK_ICD_WSI_PLATFORM_HEADLESS; + icd_surface->headless_surf.base.platform = VK_ICD_WSI_PLATFORM_HEADLESS; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateHeadlessSurfaceEXT) { - vkRes = icd_term->dispatch.CreateHeadlessSurfaceEXT(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateHeadlessSurfaceEXT(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(inst, pIcdSurface); - } - - return vkRes; + return result; } // Ensure we are properly setting VK_USE_PLATFORM_METAL_EXT, VK_USE_PLATFORM_IOS_MVK, and VK_USE_PLATFORM_MACOS_MVK. @@ -1411,61 +1389,49 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK(VkInstance // This is the instance chain terminator function for CreateMacOSSurfaceKHR VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMacOSSurfaceMVK(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_macos_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_MVK_macos_surface extension not enabled. vkCreateMacOSSurfaceMVK not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->macos_surf.base), sizeof(pIcdSurface->macos_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->macos_surf.base), sizeof(icd_surface->macos_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->macos_surf.base.platform = VK_ICD_WSI_PLATFORM_MACOS; - pIcdSurface->macos_surf.pView = pCreateInfo->pView; + icd_surface->macos_surf.base.platform = VK_ICD_WSI_PLATFORM_MACOS; + icd_surface->macos_surf.pView = pCreateInfo->pView; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateMacOSSurfaceMVK) { - vkRes = icd_term->dispatch.CreateMacOSSurfaceMVK(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateMacOSSurfaceMVK(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } #endif // VK_USE_PLATFORM_MACOS_MVK @@ -1502,16 +1468,16 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateIOSSurfaceMVK(VkInstance instanc } // Next, if so, proceed with the implementation of this function: - VkIcdSurfaceIOS *pIcdSurface = + VkIcdSurfaceIOS *icd_surface = loader_instance_heap_alloc(loader_inst, sizeof(VkIcdSurfaceIOS), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); - if (pIcdSurface == NULL) { + if (icd_surface == NULL) { return VK_ERROR_OUT_OF_HOST_MEMORY; } - pIcdSurface->base.platform = VK_ICD_WSI_PLATFORM_IOS; - pIcdSurface->pView = pCreateInfo->pView; + icd_surface->base.platform = VK_ICD_WSI_PLATFORM_IOS; + icd_surface->pView = pCreateInfo->pView; - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; return VK_SUCCESS; } @@ -1541,60 +1507,49 @@ vkCreateStreamDescriptorSurfaceGGP(VkInstance instance, const VkStreamDescriptor VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateStreamDescriptorSurfaceGGP(VkInstance instance, const VkStreamDescriptorSurfaceCreateInfoGGP *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_ggp_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_GGP_stream_descriptor_surface extension not enabled. vkCreateStreamDescriptorSurfaceGGP not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->ggp_surf.base), sizeof(pIcdSurface->ggp_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->ggp_surf.base), sizeof(icd_surface->ggp_surf), pAllocator, + &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->ggp_surf.base.platform = VK_ICD_WSI_PLATFORM_GGP; - pIcdSurface->ggp_surf.streamDescriptor = pCreateInfo->streamDescriptor; + icd_surface->ggp_surf.base.platform = VK_ICD_WSI_PLATFORM_GGP; + icd_surface->ggp_surf.streamDescriptor = pCreateInfo->streamDescriptor; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateStreamDescriptorSurfaceGGP) { - vkRes = icd_term->dispatch.CreateStreamDescriptorSurfaceGGP(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateStreamDescriptorSurfaceGGP( + icd_term->instance, pCreateInfo, pAllocator, &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - return vkRes; + return result; } #endif // VK_USE_PLATFORM_GGP @@ -1618,7 +1573,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMetalSurfaceEXT(VkInstance insta const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { VkResult result = VK_SUCCESS; VkIcdSurface *icd_surface = NULL; - uint32_t i; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); @@ -1628,9 +1583,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMetalSurfaceEXT(VkInstance insta } // Next, if so, proceed with the implementation of this function: - icd_surface = AllocateIcdSurfaceStruct(loader_inst, sizeof(icd_surface->metal_surf.base), sizeof(icd_surface->metal_surf)); - if (icd_surface == NULL) { - result = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->metal_surf.base), sizeof(icd_surface->metal_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } @@ -1638,13 +1593,12 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMetalSurfaceEXT(VkInstance insta icd_surface->metal_surf.pLayer = pCreateInfo->pLayer; // Loop through each ICD and determine if they need to create a surface - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, ++i) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { - if (icd_term->dispatch.CreateMetalSurfaceEXT != NULL) { + if (NULL != icd_term->dispatch.CreateMetalSurfaceEXT) { result = icd_term->dispatch.CreateMetalSurfaceEXT(icd_term->instance, pCreateInfo, pAllocator, - &icd_surface->real_icd_surfaces[i]); - if (result != VK_SUCCESS) { + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } @@ -1653,18 +1607,9 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMetalSurfaceEXT(VkInstance insta *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: - if (result != VK_SUCCESS && icd_surface != NULL) { - if (icd_surface->real_icd_surfaces != NULL) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, ++i) { - if (icd_surface->real_icd_surfaces[i] == VK_NULL_HANDLE && icd_term->dispatch.DestroySurfaceKHR != NULL) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, icd_surface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, icd_surface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, icd_surface); - } + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); + return result; } @@ -1690,62 +1635,50 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateScreenSurfaceQNX(VkInstance VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateScreenSurfaceQNX(VkInstance instance, const VkScreenSurfaceCreateInfoQNX *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_screen_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_QNX_screen_surface extension not enabled. vkCreateScreenSurfaceQNX not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->screen_surf.base), sizeof(pIcdSurface->screen_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->screen_surf.base), sizeof(icd_surface->screen_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->screen_surf.base.platform = VK_ICD_WSI_PLATFORM_SCREEN; - pIcdSurface->screen_surf.context = pCreateInfo->context; - pIcdSurface->screen_surf.window = pCreateInfo->window; + icd_surface->screen_surf.base.platform = VK_ICD_WSI_PLATFORM_SCREEN; + icd_surface->screen_surf.context = pCreateInfo->context; + icd_surface->screen_surf.window = pCreateInfo->window; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateScreenSurfaceQNX) { - vkRes = icd_term->dispatch.CreateScreenSurfaceQNX(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateScreenSurfaceQNX(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } // This is the trampoline entrypoint for @@ -1810,61 +1743,49 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN(VkInstance inst // This is the instance chain terminator function for CreateViSurfaceNN VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateViSurfaceNN(VkInstance instance, const VkViSurfaceCreateInfoNN *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); // First, check to ensure the appropriate extension was enabled: struct loader_instance *loader_inst = loader_get_instance(instance); if (!loader_inst->wsi_vi_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_NN_vi_surface extension not enabled. vkCreateViSurfaceNN not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->vi_surf.base), sizeof(pIcdSurface->vi_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->vi_surf.base), sizeof(icd_surface->vi_surf), pAllocator, + &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->vi_surf.base.platform = VK_ICD_WSI_PLATFORM_VI; - pIcdSurface->vi_surf.window = pCreateInfo->window; + icd_surface->vi_surf.base.platform = VK_ICD_WSI_PLATFORM_VI; + icd_surface->vi_surf.window = pCreateInfo->window; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateViSurfaceNN) { - vkRes = icd_term->dispatch.CreateViSurfaceNN(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateViSurfaceNN(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } #endif // VK_USE_PLATFORM_VI_NN @@ -2136,66 +2057,55 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDisplayPlaneSurfaceKHR(VkInstanc const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - struct loader_instance *inst = loader_get_instance(instance); - VkIcdSurface *pIcdSurface = NULL; - VkResult vkRes = VK_SUCCESS; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; + loader_platform_thread_lock_mutex(&loader_lock); - if (!inst->wsi_display_enabled) { - loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, + // First, check to ensure the appropriate extension was enabled: + struct loader_instance *loader_inst = loader_get_instance(instance); + if (!loader_inst->wsi_display_enabled) { + loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_KHR_surface extension not enabled. vkCreateDisplayPlaneSurfaceKHR not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = AllocateIcdSurfaceStruct(inst, sizeof(pIcdSurface->display_surf.base), sizeof(pIcdSurface->display_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->display_surf.base), sizeof(icd_surface->display_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->display_surf.base.platform = VK_ICD_WSI_PLATFORM_DISPLAY; - pIcdSurface->display_surf.displayMode = pCreateInfo->displayMode; - pIcdSurface->display_surf.planeIndex = pCreateInfo->planeIndex; - pIcdSurface->display_surf.planeStackIndex = pCreateInfo->planeStackIndex; - pIcdSurface->display_surf.transform = pCreateInfo->transform; - pIcdSurface->display_surf.globalAlpha = pCreateInfo->globalAlpha; - pIcdSurface->display_surf.alphaMode = pCreateInfo->alphaMode; - pIcdSurface->display_surf.imageExtent = pCreateInfo->imageExtent; + icd_surface->display_surf.base.platform = VK_ICD_WSI_PLATFORM_DISPLAY; + icd_surface->display_surf.displayMode = pCreateInfo->displayMode; + icd_surface->display_surf.planeIndex = pCreateInfo->planeIndex; + icd_surface->display_surf.planeStackIndex = pCreateInfo->planeStackIndex; + icd_surface->display_surf.transform = pCreateInfo->transform; + icd_surface->display_surf.globalAlpha = pCreateInfo->globalAlpha; + icd_surface->display_surf.alphaMode = pCreateInfo->alphaMode; + icd_surface->display_surf.imageExtent = pCreateInfo->imageExtent; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateDisplayPlaneSurfaceKHR) { - vkRes = icd_term->dispatch.CreateDisplayPlaneSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateDisplayPlaneSurfaceKHR(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); + loader_platform_thread_unlock_mutex(&loader_lock); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(inst, pIcdSurface); - } - - return vkRes; + return result; } // EXT_display_swapchain Extension command @@ -2217,9 +2127,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateSharedSwapchainsKHR(VkDevice dev const VkSwapchainCreateInfoKHR *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchains) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "vkCreateSharedSwapchainsKHR Terminator: Invalid device handle. This is likely the result of a " @@ -2227,29 +2136,32 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateSharedSwapchainsKHR(VkDevice dev "[VUID-vkCreateSharedSwapchainsKHR-device-parameter]"); abort(); /* Intentionally fail so user can correct issue. */ } + if (NULL != icd_term->surface_list.list) { + loader_log(NULL, VULKAN_LOADER_ERROR_BIT, 0, + "vkCreateSharedSwapchainsKHR Terminator: No VkSurfaceKHR objects were created, indicating an application " + "bug. Returning VK_SUCCESS. "); + return VK_SUCCESS; + } if (NULL == dev->loader_dispatch.extension_terminator_dispatch.CreateSharedSwapchainsKHR) { loader_log(NULL, VULKAN_LOADER_ERROR_BIT, 0, - "vkCreateSharedSwapchainsKHR: Driver's function pointer was NULL, returning VK_SUCCESS. Was the " + "vkCreateSharedSwapchainsKHR Terminator: Driver's function pointer was NULL, returning VK_SUCCESS. Was the " "VK_KHR_display_swapchain extension enabled?"); return VK_SUCCESS; } - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pCreateInfos->surface; - if ((VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) { - // We found the ICD, and there is an ICD KHR surface - // associated with it, so copy the CreateInfo struct - // and point it at the ICD's surface. - VkSwapchainCreateInfoKHR *pCreateCopy = loader_stack_alloc(sizeof(VkSwapchainCreateInfoKHR) * swapchainCount); - if (NULL == pCreateCopy) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - memcpy(pCreateCopy, pCreateInfos, sizeof(VkSwapchainCreateInfoKHR) * swapchainCount); - for (uint32_t sc = 0; sc < swapchainCount; sc++) { - pCreateCopy[sc].surface = icd_surface->real_icd_surfaces[icd_index]; + + VkSwapchainCreateInfoKHR *pCreateCopy = loader_stack_alloc(sizeof(VkSwapchainCreateInfoKHR) * swapchainCount); + if (NULL == pCreateCopy) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + memcpy(pCreateCopy, pCreateInfos, sizeof(VkSwapchainCreateInfoKHR) * swapchainCount); + for (uint32_t sc = 0; sc < swapchainCount; sc++) { + VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pCreateCopy[sc].surface; + if (icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { + pCreateCopy[sc].surface = icd_term->surface_list.list[icd_surface->surface_index]; } - return dev->loader_dispatch.extension_terminator_dispatch.CreateSharedSwapchainsKHR(device, swapchainCount, pCreateCopy, - pAllocator, pSwapchains); } - return dev->loader_dispatch.extension_terminator_dispatch.CreateSharedSwapchainsKHR(device, swapchainCount, pCreateInfos, + return dev->loader_dispatch.extension_terminator_dispatch.CreateSharedSwapchainsKHR(device, swapchainCount, pCreateCopy, pAllocator, pSwapchains); } @@ -2279,9 +2191,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModes VKAPI_ATTR VkResult VKAPI_CALL terminator_GetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR *pModes) { - uint32_t icd_index = 0; struct loader_device *dev; - struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index); + struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); if (NULL == icd_term || NULL == dev) { loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "vkGetDeviceGroupSurfacePresentModesKHR: Invalid device " @@ -2296,9 +2207,11 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetDeviceGroupSurfacePresentModesKHR(V return VK_SUCCESS; } VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface; - if (NULL != icd_surface->real_icd_surfaces && (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) { + if (NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { return dev->loader_dispatch.extension_terminator_dispatch.GetDeviceGroupSurfacePresentModesKHR( - device, icd_surface->real_icd_surfaces[icd_index], pModes); + device, icd_term->surface_list.list[icd_surface->surface_index], pModes); } return dev->loader_dispatch.extension_terminator_dispatch.GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes); } @@ -2333,10 +2246,11 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDevicePresentRectanglesKHR( return VK_SUCCESS; } VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(surface); - uint8_t icd_index = phys_dev_term->icd_index; - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)(icd_surface->real_icd_surfaces[icd_index])) { + if (NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { return icd_term->dispatch.GetPhysicalDevicePresentRectanglesKHR( - phys_dev_term->phys_dev, icd_surface->real_icd_surfaces[icd_index], pRectCount, pRects); + phys_dev_term->phys_dev, icd_term->surface_list.list[icd_surface->surface_index], pRectCount, pRects); } return icd_term->dispatch.GetPhysicalDevicePresentRectanglesKHR(phys_dev_term->phys_dev, surface, pRectCount, pRects); } @@ -2587,9 +2501,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateImagePipeSurfaceFUCHSIA(VkInstan const VkImagePipeSurfaceCreateInfoFUCHSIA *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - VkResult vkRes = VK_SUCCESS; - VkIcdSurface *pIcdSurface = NULL; - uint32_t i = 0; + VkResult result = VK_SUCCESS; + VkIcdSurface *icd_surface = NULL; // Initialize pSurface to NULL just to be safe. *pSurface = VK_NULL_HANDLE; @@ -2599,52 +2512,38 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateImagePipeSurfaceFUCHSIA(VkInstan loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, "VK_FUCHSIA_imagepipe_surface extension not enabled. " "vkCreateImagePipeSurfaceFUCHSIA not executed!"); - vkRes = VK_ERROR_EXTENSION_NOT_PRESENT; + result = VK_ERROR_EXTENSION_NOT_PRESENT; goto out; } // Next, if so, proceed with the implementation of this function: - pIcdSurface = - AllocateIcdSurfaceStruct(loader_inst, sizeof(pIcdSurface->imagepipe_surf.base), sizeof(pIcdSurface->imagepipe_surf)); - if (pIcdSurface == NULL) { - vkRes = VK_ERROR_OUT_OF_HOST_MEMORY; + result = allocate_icd_surface_struct(loader_inst, sizeof(icd_surface->imagepipe_surf.base), sizeof(icd_surface->imagepipe_surf), + pAllocator, &icd_surface); + if (VK_SUCCESS != result) { goto out; } - pIcdSurface->imagepipe_surf.base.platform = VK_ICD_WSI_PLATFORM_FUCHSIA; + icd_surface->imagepipe_surf.base.platform = VK_ICD_WSI_PLATFORM_FUCHSIA; // Loop through each ICD and determine if they need to create a surface - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { + for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next) { if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) { if (NULL != icd_term->dispatch.CreateImagePipeSurfaceFUCHSIA) { - vkRes = icd_term->dispatch.CreateImagePipeSurfaceFUCHSIA(icd_term->instance, pCreateInfo, pAllocator, - &pIcdSurface->real_icd_surfaces[i]); - if (VK_SUCCESS != vkRes) { + result = icd_term->dispatch.CreateImagePipeSurfaceFUCHSIA(icd_term->instance, pCreateInfo, pAllocator, + &icd_term->surface_list.list[icd_surface->surface_index]); + if (VK_SUCCESS != result) { goto out; } } } } - *pSurface = (VkSurfaceKHR)(uintptr_t)pIcdSurface; + *pSurface = (VkSurfaceKHR)(uintptr_t)icd_surface; out: + cleanup_surface_creation(loader_inst, result, icd_surface, pAllocator); - if (VK_SUCCESS != vkRes && NULL != pIcdSurface) { - if (NULL != pIcdSurface->real_icd_surfaces) { - i = 0; - for (struct loader_icd_term *icd_term = loader_inst->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) { - if ((VkSurfaceKHR)(uintptr_t)NULL != pIcdSurface->real_icd_surfaces[i] && - NULL != icd_term->dispatch.DestroySurfaceKHR) { - icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator); - } - } - loader_instance_heap_free(loader_inst, pIcdSurface->real_icd_surfaces); - } - loader_instance_heap_free(loader_inst, pIcdSurface); - } - - return vkRes; + return result; } #endif // VK_USE_PLATFORM_FUCHSIA @@ -2663,12 +2562,71 @@ vkGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice, cons return disp->GetPhysicalDeviceSurfaceCapabilities2KHR(unwrapped_phys_dev, pSurfaceInfo, pSurfaceCapabilities); } +void emulate_VK_EXT_surface_maintenance1(struct loader_icd_term *icd_term, const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, + VkSurfaceCapabilities2KHR *pSurfaceCapabilities) { + // Because VK_EXT_surface_maintenance1 is an instance extension, applications will use it to query info on drivers which do + // not support the extension. Thus we need to emulate the driver filling out the structs in that case. + if (!icd_term->supports_ext_surface_maintenance_1) { + VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAX_ENUM_KHR; + const void *void_pNext = pSurfaceInfo->pNext; + while (void_pNext) { + VkBaseOutStructure out_structure = {0}; + memcpy(&out_structure, void_pNext, sizeof(VkBaseOutStructure)); + if (out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT) { + VkSurfacePresentModeEXT *surface_present_mode = (VkSurfacePresentModeEXT *)void_pNext; + present_mode = surface_present_mode->presentMode; + } + void_pNext = out_structure.pNext; + } + // If no VkSurfacePresentModeEXT was present, return + if (present_mode == VK_PRESENT_MODE_MAX_ENUM_KHR) { + return; + } + + void_pNext = pSurfaceCapabilities->pNext; + while (void_pNext) { + VkBaseOutStructure out_structure = {0}; + memcpy(&out_structure, void_pNext, sizeof(VkBaseOutStructure)); + if (out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT) { + VkSurfacePresentModeCompatibilityEXT *surface_present_mode_compatibility = + (VkSurfacePresentModeCompatibilityEXT *)void_pNext; + if (surface_present_mode_compatibility->pPresentModes) { + if (surface_present_mode_compatibility->presentModeCount != 0) { + surface_present_mode_compatibility->pPresentModes[0] = present_mode; + surface_present_mode_compatibility->presentModeCount = 1; + } + } else { + surface_present_mode_compatibility->presentModeCount = 1; + } + + } else if (out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT) { + // Because there is no way to fill out the information faithfully, set scaled max/min image extent to the + // surface capabilities max/min extent and the rest to zero. + VkSurfacePresentScalingCapabilitiesEXT *surface_present_scaling_capabilities = + (VkSurfacePresentScalingCapabilitiesEXT *)void_pNext; + surface_present_scaling_capabilities->supportedPresentScaling = 0; + surface_present_scaling_capabilities->supportedPresentGravityX = 0; + surface_present_scaling_capabilities->supportedPresentGravityY = 0; + surface_present_scaling_capabilities->maxScaledImageExtent = + pSurfaceCapabilities->surfaceCapabilities.maxImageExtent; + surface_present_scaling_capabilities->minScaledImageExtent = + pSurfaceCapabilities->surfaceCapabilities.minImageExtent; + } + void_pNext = out_structure.pNext; + } + } +} + VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, VkSurfaceCapabilities2KHR *pSurfaceCapabilities) { struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)physicalDevice; struct loader_icd_term *icd_term = phys_dev_term->this_icd_term; struct loader_instance *loader_inst = (struct loader_instance *)icd_term->this_instance; + VkIcdSurface *icd_surface = NULL; + if (pSurfaceInfo->surface) { + icd_surface = (VkIcdSurface *)(uintptr_t)pSurfaceInfo->surface; + } if (!loader_inst->wsi_surface_enabled) { loader_log(loader_inst, VULKAN_LOADER_ERROR_BIT, 0, @@ -2676,31 +2634,42 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2K return VK_SUCCESS; } - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(pSurfaceInfo->surface); - uint8_t icd_index = phys_dev_term->icd_index; - if (icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR != NULL) { - VkBaseOutStructure *pNext = (VkBaseOutStructure *)pSurfaceCapabilities->pNext; + void *pNext = pSurfaceCapabilities->pNext; while (pNext != NULL) { - if ((int)pNext->sType == VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR) { + VkBaseOutStructure pNext_out_structure = {0}; + memcpy(&pNext_out_structure, pNext, sizeof(VkBaseOutStructure)); + if (pNext_out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR) { // Not all ICDs may be supporting VK_KHR_surface_protected_capabilities // Initialize VkSurfaceProtectedCapabilitiesKHR.supportsProtected to false and // if an ICD supports protected surfaces, it will reset it to true accordingly. ((VkSurfaceProtectedCapabilitiesKHR *)pNext)->supportsProtected = VK_FALSE; } - pNext = (VkBaseOutStructure *)pNext->pNext; + pNext = pNext_out_structure.pNext; } + VkResult res = VK_SUCCESS; + // Pass the call to the driver, possibly unwrapping the ICD surface - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)icd_surface->real_icd_surfaces[icd_index]) { + if (NULL != icd_surface && NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { VkPhysicalDeviceSurfaceInfo2KHR info_copy = *pSurfaceInfo; - info_copy.surface = icd_surface->real_icd_surfaces[icd_index]; - return icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, &info_copy, - pSurfaceCapabilities); + info_copy.surface = icd_term->surface_list.list[icd_surface->surface_index]; + res = icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, &info_copy, + pSurfaceCapabilities); } else { - return icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, pSurfaceInfo, - pSurfaceCapabilities); + res = icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, pSurfaceInfo, + pSurfaceCapabilities); + } + + // Because VK_EXT_surface_maintenance1 is an instance extension, applications will use it to query info on drivers which do + // not support the extension. Thus we need to emulate the driver filling out the structs in that case. + if (!icd_term->supports_ext_surface_maintenance_1) { + emulate_VK_EXT_surface_maintenance1(icd_term, pSurfaceInfo, pSurfaceCapabilities); } + + return res; } else { // Emulate the call loader_log(icd_term->this_instance, VULKAN_LOADER_INFO_BIT, 0, @@ -2708,16 +2677,12 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2K "vkGetPhysicalDeviceSurfaceCapabilitiesKHR", icd_term->scanned_icd->lib_name); - if (pSurfaceInfo->pNext != NULL) { - loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0, - "vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulation found unrecognized structure type in " - "pSurfaceInfo->pNext - this struct will be ignored"); - } - // Write to the VkSurfaceCapabilities2KHR struct - VkSurfaceKHR surface = pSurfaceInfo->surface; - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)(icd_surface->real_icd_surfaces[icd_index])) { - surface = icd_surface->real_icd_surfaces[icd_index]; + VkSurfaceKHR surface = VK_NULL_HANDLE; + if (NULL != icd_surface && NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { + surface = icd_term->surface_list.list[icd_surface->surface_index]; } // If the icd doesn't support VK_KHR_surface, then there are no capabilities @@ -2730,11 +2695,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2K VkResult res = icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR(phys_dev_term->phys_dev, surface, &pSurfaceCapabilities->surfaceCapabilities); - if (pSurfaceCapabilities->pNext != NULL) { - loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0, - "vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulation found unrecognized structure type in " - "pSurfaceCapabilities->pNext - this struct will be ignored"); - } + emulate_VK_EXT_surface_maintenance1(icd_term, pSurfaceInfo, pSurfaceCapabilities); return res; } } @@ -2768,14 +2729,18 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceFormats2KHR(Vk return VK_SUCCESS; } - VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)(pSurfaceInfo->surface); - uint8_t icd_index = phys_dev_term->icd_index; + VkIcdSurface *icd_surface = NULL; + if (VK_NULL_HANDLE != pSurfaceInfo->surface) { + icd_surface = (VkIcdSurface *)(uintptr_t)(pSurfaceInfo->surface); + } if (icd_term->dispatch.GetPhysicalDeviceSurfaceFormats2KHR != NULL) { // Pass the call to the driver, possibly unwrapping the ICD surface - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)(icd_surface->real_icd_surfaces[icd_index])) { + if (NULL != icd_surface && NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { VkPhysicalDeviceSurfaceInfo2KHR info_copy = *pSurfaceInfo; - info_copy.surface = icd_surface->real_icd_surfaces[icd_index]; + info_copy.surface = icd_term->surface_list.list[icd_surface->surface_index]; return icd_term->dispatch.GetPhysicalDeviceSurfaceFormats2KHR(phys_dev_term->phys_dev, &info_copy, pSurfaceFormatCount, pSurfaceFormats); } else { @@ -2795,8 +2760,10 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceFormats2KHR(Vk } VkSurfaceKHR surface = pSurfaceInfo->surface; - if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)(uintptr_t)(icd_surface->real_icd_surfaces[icd_index])) { - surface = icd_surface->real_icd_surfaces[icd_index]; + if (NULL != icd_surface && NULL != icd_term->surface_list.list && + icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && + icd_term->surface_list.list[icd_surface->surface_index]) { + surface = icd_term->surface_list.list[icd_surface->surface_index]; } // If the icd doesn't support VK_KHR_surface, then there are no formats diff --git a/loader/wsi.h b/loader/wsi.h index a84df04e14def51b317daaa421c5db5a0722531a..e8e3ac03fab1fabfaee1fa1cd0fcb68d23f35e0e 100644 --- a/loader/wsi.h +++ b/loader/wsi.h @@ -25,6 +25,10 @@ #include "loader_common.h" typedef struct { + // This union holds the data that drivers which use ICD interface version 2 and before expect. This is so they can dereference + // VkSurfaceKHR to get this struct and access the creation parameters used in subsequent API calls, such as get surface formats + // & get surface present modes. + // Thus, these members need to stay here in order to preserve ABI compatibility. union { #if defined(VK_USE_PLATFORM_WAYLAND_KHR) VkIcdSurfaceWayland wayland_surf; @@ -66,7 +70,7 @@ typedef struct { uint32_t platform_size; // Size of corresponding VkIcdSurfaceXXX uint32_t non_platform_offset; // Start offset to base_size uint32_t entire_size; // Size of entire VkIcdSurface - VkSurfaceKHR *real_icd_surfaces; + uint32_t surface_index; // This surface's index into each drivers list of created surfaces } VkIcdSurface; bool wsi_swapchain_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr); diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 22fe9b2925c9d3dd5f352314742ccb64db0b7abc..f52f3dcd655081b2d7ce28bd7170d1461ec4434e 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -65,7 +65,7 @@ if (UPDATE_DEPS) math(EXPR bitness "8 * ${CMAKE_SIZEOF_VOID_P}") set(UPDATE_DEPS_DIR_SUFFIX "${UPDATE_DEPS_DIR_SUFFIX}/${bitness}") endif() - endif() + endif() set(UPDATE_DEPS_DIR "${PROJECT_SOURCE_DIR}/external/${UPDATE_DEPS_DIR_SUFFIX}" CACHE PATH "Location where update_deps.py installs packages") list(APPEND update_dep_command "--dir" ) list(APPEND update_dep_command "${UPDATE_DEPS_DIR}") @@ -139,8 +139,8 @@ if (VULKAN_HEADERS_INSTALL_DIR) set(CMAKE_REQUIRE_FIND_PACKAGE_VulkanHeaders TRUE PARENT_SCOPE) endif() +set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) + if (CMAKE_CROSSCOMPILING) set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CMAKE_PREFIX_PATH} PARENT_SCOPE) -else() - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) endif() diff --git a/scripts/CodeCoverage.cmake b/scripts/CodeCoverage.cmake new file mode 100644 index 0000000000000000000000000000000000000000..4b9ca0e84fbe580f3973cc570768149291a6bf21 --- /dev/null +++ b/scripts/CodeCoverage.cmake @@ -0,0 +1,665 @@ +# +# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# USAGE: To enable any code coverage instrumentation/targets, the single CMake +# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or +# on the command line. +# +# From this point, there are two primary methods for adding instrumentation to +# targets: 1 - A blanket instrumentation by calling `add_code_coverage()`, where +# all targets in that directory and all subdirectories are automatically +# instrumented. 2 - Per-target instrumentation by calling +# `target_code_coverage()`, where the target is given and thus only +# that target is instrumented. This applies to both libraries and executables. +# +# To add coverage targets, such as calling `make ccov` to generate the actual +# coverage information for perusal or consumption, call +# `target_code_coverage()` on an *executable* target. +# +# Example 1: All targets instrumented +# +# In this case, the coverage information reported will will be that of the +# `theLib` library target and `theExe` executable. +# +# 1a: Via global command +# +# ~~~ +# add_code_coverage() # Adds instrumentation to all targets +# +# add_library(theLib lib.cpp) +# +# add_executable(theExe main.cpp) +# target_link_libraries(theExe PRIVATE theLib) +# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target (instrumentation already added via global anyways) for generating code coverage reports. +# ~~~ +# +# 1b: Via target commands +# +# ~~~ +# add_library(theLib lib.cpp) +# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets. +# +# add_executable(theExe main.cpp) +# target_link_libraries(theExe PRIVATE theLib) +# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports. +# ~~~ +# +# Example 2: Target instrumented, but with regex pattern of files to be excluded +# from report +# +# ~~~ +# add_executable(theExe main.cpp non_covered.cpp) +# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder. +# ~~~ +# +# Example 3: Target added to the 'ccov' and 'ccov-all' targets +# +# ~~~ +# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders. +# +# add_executable(theExe main.cpp non_covered.cpp) +# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. +# ~~~ + +# Options +# option( +# CODE_COVERAGE +# "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" +# OFF) + +# Programs +find_program(LLVM_COV_PATH llvm-cov) +find_program(LLVM_PROFDATA_PATH llvm-profdata) +find_program(LCOV_PATH lcov) +find_program(GENHTML_PATH genhtml) +# Hide behind the 'advanced' mode flag for GUI/ccmake +mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH) + +# Variables +set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov) +set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1) + +# Common initialization/checks +if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) + set(CODE_COVERAGE_ADDED ON) + + # Common Targets + add_custom_target( + ccov-preprocessing + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY} + DEPENDS ccov-clean) + + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + # Messages + message(STATUS "Building with llvm Code Coverage Tools") + + if(NOT LLVM_COV_PATH) + message(FATAL_ERROR "llvm-cov not found! Aborting.") + else() + # Version number checking for 'EXCLUDE' compatibility + execute_process(COMMAND ${LLVM_COV_PATH} --version + OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION + ${LLVM_COV_VERSION_CALL_OUTPUT}) + + if(LLVM_COV_VERSION VERSION_LESS "7.0.0") + message( + WARNING + "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0" + ) + endif() + endif() + + # Targets + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-clean + COMMAND ${CMAKE_COMMAND} -E remove -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND ${CMAKE_COMMAND} -E remove -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) + else() + add_custom_target( + ccov-clean + COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) + endif() + + # Used to get the shared object file list before doing the main all- + # processing + add_custom_target( + ccov-libs + COMMAND ; + COMMENT "libs ready for coverage report.") + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + # Messages + message(STATUS "Building with lcov Code Coverage Tools") + + if(CMAKE_BUILD_TYPE) + string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) + if(NOT ${upper_build_type} STREQUAL "DEBUG") + message( + WARNING + "Code coverage results with an optimized (non-Debug) build may be misleading" + ) + endif() + else() + message( + WARNING + "Code coverage results with an optimized (non-Debug) build may be misleading" + ) + endif() + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Targets + add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory + ${CMAKE_BINARY_DIR} --zerocounters) + + else() + message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") + endif() +endif() + +# Adds code coverage instrumentation to a library, or instrumentation/targets +# for an executable target. +# ~~~ +# EXECUTABLE ADDED TARGETS: +# GCOV/LCOV: +# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target. +# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. +# +# LLVM-COV: +# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-${TARGET_NAME} : Generates HTML code coverage report. +# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information. +# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file. +# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information. +# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. +# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line. +# ccov-all-export : Exports the coverage report to a JSON file. +# +# Required: +# TARGET_NAME - Name of the target to generate code coverage for. +# Optional: +# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. +# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. +# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. +# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. +# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory +# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`. +# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.** +# OBJECTS - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output +# ARGS - For executables ONLY, appends the given arguments to the associated ccov-* executable call +# ~~~ +function(target_code_coverage TARGET_NAME) + # Argument parsing + set(options AUTO ALL EXTERNAL PUBLIC INTERFACE) + set(single_value_keywords COVERAGE_TARGET_NAME) + set(multi_value_keywords EXCLUDE OBJECTS ARGS) + cmake_parse_arguments( + target_code_coverage "${options}" "${single_value_keywords}" + "${multi_value_keywords}" ${ARGN}) + + # Set the visibility of target functions to PUBLIC, INTERFACE or default to + # PRIVATE. + if(target_code_coverage_PUBLIC) + set(TARGET_VISIBILITY PUBLIC) + elseif(target_code_coverage_INTERFACE) + set(TARGET_VISIBILITY INTERFACE) + else() + set(TARGET_VISIBILITY PRIVATE) + endif() + + if(NOT target_code_coverage_COVERAGE_TARGET_NAME) + # If a specific name was given, use that instead. + set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME}) + endif() + + if(CODE_COVERAGE) + + # Add code coverage instrumentation to the target's linker command + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} + -fprofile-instr-generate -fcoverage-mapping) + target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY} + -fprofile-instr-generate -fcoverage-mapping) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs + -ftest-coverage) + target_link_libraries(${TARGET_NAME} ${TARGET_VISIBILITY} gcov) + endif() + + # Targets + get_target_property(target_type ${TARGET_NAME} TYPE) + + # Add shared library to processing for 'all' targets + if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${CMAKE_COMMAND} -E echo "-object=$" >> + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + DEPENDS ccov-preprocessing ${TARGET_NAME}) + + if(NOT TARGET ccov-libs) + message( + FATAL_ERROR + "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." + ) + endif() + + add_dependencies(ccov-libs + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + + # For executables add targets to run and produce output + if(target_type STREQUAL "EXECUTABLE") + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + + # If there are shared objects to also work with, generate the string to + # add them here + foreach(SO_TARGET ${target_code_coverage_OBJECTS}) + # Check to see if the target is a shared object + if(TARGET ${SO_TARGET}) + get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE) + if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY") + set(SO_OBJECTS ${SO_OBJECTS} -object=$) + endif() + endif() + endforeach() + + # Run the executable, generating raw profile data Make the run data + # available for further processing. Separated to allow Windows to run + # this target serially. + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${CMAKE_COMMAND} -E env + LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw + $ ${target_code_coverage_ARGS} + COMMAND + ${CMAKE_COMMAND} -E echo "-object=$" + ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND + ${CMAKE_COMMAND} -E echo + "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw" + >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list + JOB_POOL ccov_serial_pool + DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME}) + + # Merge the generated profile data so llvm-cov can process it + add_custom_target( + ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_PROFDATA_PATH} merge -sparse + ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o + ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Ignore regex only works on LLVM >= 7 + if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") + foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} + -ignore-filename-regex='${EXCLUDE_ITEM}') + endforeach() + endif() + + # Print out details of the coverage information to the command line + add_custom_target( + ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} show $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -show-line-counts-or-regions ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Print out a summary of the coverage information to the command line + add_custom_target( + ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} report $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Export coverage information so continuous integration tools (e.g. + # Jenkins) can consume it + add_custom_target( + ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} export $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -format="text" ${EXCLUDE_REGEX} > + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Generates HTML output of the coverage information for perusal + add_custom_target( + ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} show $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + set(COVERAGE_INFO + "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info" + ) + + # Run the executable, generating coverage information + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND $ ${target_code_coverage_ARGS} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + + # Generate exclusion string for use + foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} + '${EXCLUDE_ITEM}') + endforeach() + + if(EXCLUDE_REGEX) + set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file + ${COVERAGE_INFO}) + else() + set(EXCLUDE_COMMAND ;) + endif() + + if(NOT ${target_code_coverage_EXTERNAL}) + set(EXTERNAL_OPTION --no-external) + endif() + + # Capture coverage data + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMAND $ ${target_code_coverage_ARGS} + COMMAND + ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory + ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file + ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + else() + add_custom_target( + ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMAND $ ${target_code_coverage_ARGS} + COMMAND + ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory + ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file + ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + endif() + + # Generates HTML output of the coverage information for perusal + add_custom_target( + ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${GENHTML_PATH} -o + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} + ${COVERAGE_INFO} + DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + + add_custom_command( + TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + POST_BUILD + COMMAND ; + COMMENT + "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report." + ) + + # AUTO + if(target_code_coverage_AUTO) + if(NOT TARGET ccov) + add_custom_target(ccov) + endif() + add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME}) + + if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID + MATCHES "GNU") + if(NOT TARGET ccov-report) + add_custom_target(ccov-report) + endif() + add_dependencies( + ccov-report + ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + + # ALL + if(target_code_coverage_ALL) + if(NOT TARGET ccov-all-processing) + message( + FATAL_ERROR + "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." + ) + endif() + + add_dependencies(ccov-all-processing + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + endif() +endfunction() + +# Adds code coverage instrumentation to all targets in the current directory and +# any subdirectories. To add coverage instrumentation to only specific targets, +# use `target_code_coverage`. +function(add_code_coverage) + if(CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + add_compile_options(-fprofile-instr-generate -fcoverage-mapping) + add_link_options(-fprofile-instr-generate -fcoverage-mapping) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + add_compile_options(-fprofile-arcs -ftest-coverage) + link_libraries(gcov) + endif() + endif() +endfunction() + +# Adds the 'ccov-all' type targets that calls all targets added via +# `target_code_coverage` with the `ALL` parameter, but merges all the coverage +# data from them into a single large report instead of the numerous smaller +# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for +# use with coverage dashboards (e.g. codecov.io, coveralls). +# ~~~ +# Optional: +# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! +# ~~~ +function(add_code_coverage_all_targets) + # Argument parsing + set(multi_value_keywords EXCLUDE) + cmake_parse_arguments(add_code_coverage_all_targets "" "" + "${multi_value_keywords}" ${ARGN}) + + if(CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + + # Merge the profile data for all of the run executables + if(WIN32) + add_custom_target( + ccov-all-processing + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe + merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -sparse $$FILELIST) + else() + add_custom_target( + ccov-all-processing + COMMAND + ${LLVM_PROFDATA_PATH} merge -o + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`) + endif() + + # Regex exclude only available for LLVM >= 7 + if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") + foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} + -ignore-filename-regex='${EXCLUDE_ITEM}') + endforeach() + endif() + + # Print summary of the code coverage information to the command line + if(WIN32) + add_custom_target( + ccov-all-report + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe + report $$FILELIST + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + else() + add_custom_target( + ccov-all-report + COMMAND + ${LLVM_COV_PATH} report `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + endif() + + # Export coverage information so continuous integration tools (e.g. + # Jenkins) can consume it + add_custom_target( + ccov-all-export + COMMAND + ${LLVM_COV_PATH} export `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -format="text" ${EXCLUDE_REGEX} > + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json + DEPENDS ccov-all-processing) + + # Generate HTML output of all added targets for perusal + if(WIN32) + add_custom_target( + ccov-all + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show + $$FILELIST + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + else() + add_custom_target( + ccov-all + COMMAND + ${LLVM_COV_PATH} show `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + endif() + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info") + + # Nothing required for gcov + add_custom_target(ccov-all-processing COMMAND ;) + + # Exclusion regex string creation + set(EXCLUDE_REGEX) + foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} + '${EXCLUDE_ITEM}') + endforeach() + + if(EXCLUDE_REGEX) + set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file + ${COVERAGE_INFO}) + else() + set(EXCLUDE_COMMAND ;) + endif() + + # Capture coverage data + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-all-capture + COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture + --output-file ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ccov-all-processing) + else() + add_custom_target( + ccov-all-capture + COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture + --output-file ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ccov-all-processing) + endif() + + # Generates HTML output of all targets for perusal + add_custom_target( + ccov-all + COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR} + DEPENDS ccov-all-capture) + + endif() + + add_custom_command( + TARGET ccov-all + POST_BUILD + COMMAND ; + COMMENT + "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report." + ) + endif() +endfunction() diff --git a/scripts/generate_loader_rc.py b/scripts/generate_loader_rc.py new file mode 100644 index 0000000000000000000000000000000000000000..8414cf254773052556a559ba2cd8f0987e5a98d4 --- /dev/null +++ b/scripts/generate_loader_rc.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# Copyright 2023 The Khronos Group Inc. +# Copyright 2023 Valve Corporation +# Copyright 2023 LunarG, Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import sys + +def main(argv): + parser = argparse.ArgumentParser(description='Update Loader.rc for official builds') + parser.add_argument('src') + parser.add_argument('dst') + parser.add_argument('--is_official', action='store_true') + parser.set_defaults(is_official=False) + args = parser.parse_args(argv) + with open(args.src, 'r') as src: + with open(args.dst, 'w') as dst: + for line in src: + if args.is_official: + if line.startswith('#define VER_FILE_DESCRIPTION_STR'): + dst.write(line.replace('Dev Build', '0')) + elif line.startswith('#define VER_FILE_VERSION_STR'): + dst.write(line.replace(' - Dev Build', '')) + else: + dst.write(line) + else: + dst.write(line) +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/scripts/generate_source.py b/scripts/generate_source.py old mode 100755 new mode 100644 index e301ff43f4516451f65b4ce582f9050694815338..f1dbfc848deab486967492c4a56ae0b5c0c6d239 --- a/scripts/generate_source.py +++ b/scripts/generate_source.py @@ -44,8 +44,15 @@ def main(argv): group.add_argument('-v', '--verify', action='store_true', help='verify repo files match generator output') args = parser.parse_args(argv) + registry = os.path.abspath(os.path.join(args.registry, 'vk.xml')) + if not os.path.isfile(registry): + registry = os.path.abspath(os.path.join(args.registry, 'Vulkan-Headers/registry/vk.xml')) + if not os.path.isfile(registry): + print(f'cannot find vk.xml in {args.registry}') + return -1 + gen_cmds = [[common_codegen.repo_relative('scripts/loader_genvk.py'), - '-registry', os.path.abspath(os.path.join(args.registry, 'vk.xml')), + '-registry', registry, '-quiet', filename] for filename in ['vk_layer_dispatch_table.h', 'vk_loader_extensions.h', diff --git a/scripts/known_good.json b/scripts/known_good.json index 7bb67421892c4926f42d74f6fcd7ed9e2103e120..017003fb50e01345b8f20d0ae719f857f15f0305 100644 --- a/scripts/known_good.json +++ b/scripts/known_good.json @@ -7,7 +7,7 @@ "sub_dir": "Vulkan-Headers", "build_dir": "Vulkan-Headers/build", "install_dir": "Vulkan-Headers/build/install", - "commit": "v1.3.275" + "commit": "v1.4.309" }, { "name": "googletest", @@ -28,7 +28,7 @@ "build_dir": "detours", "install_dir": "detours", "build_step": "skip", - "commit": "v4.0.1", + "commit": "4b8c659f549b0ab21cf649377c7a84eb708f5e68", "optional": [ "tests" ], @@ -42,4 +42,4 @@ "googletest": "GOOGLETEST_INSTALL_DIR", "detours": "DETOURS_INSTALL_DIR" } -} +} \ No newline at end of file diff --git a/scripts/loader_extension_generator.py b/scripts/loader_extension_generator.py index 4040a9ba0c31b7fae3daddf22464ac272053e63e..46ff0d3bf1a0faefa3c6e336c0dce56bc778f671 100644 --- a/scripts/loader_extension_generator.py +++ b/scripts/loader_extension_generator.py @@ -22,10 +22,10 @@ # Author: Mark Young # Author: Mark Lobodzinski -import os,re,sys -import xml.etree.ElementTree as etree -from generator import * +import re +import sys from collections import namedtuple +from generator import * from common_codegen import * @@ -193,7 +193,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): OutputGenerator.beginFile(self, genOpts) # User-supplied prefix text, if any (list of strings) - if (genOpts.prefixText): + if genOpts.prefixText: for s in genOpts.prefixText: write(s, file=self.outFile) @@ -232,12 +232,18 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if self.genOpts.filename == 'vk_loader_extensions.h': preamble += '#pragma once\n' + preamble += '\n' + preamble += '#include \n' + preamble += '#include \n' + preamble += '#include \n' + preamble += '#include "vk_layer_dispatch_table.h"\n' + preamble += '\n' + elif self.genOpts.filename == 'vk_loader_extensions.c': preamble += '#include \n' preamble += '#include \n' preamble += '#include \n' - preamble += '#include "vk_loader_platform.h"\n' preamble += '#include "loader.h"\n' preamble += '#include "vk_loader_extensions.h"\n' preamble += '#include \n' @@ -248,6 +254,8 @@ class LoaderExtensionOutputGenerator(OutputGenerator): elif self.genOpts.filename == 'vk_layer_dispatch_table.h': preamble += '#pragma once\n' preamble += '\n' + preamble += '#include \n' + preamble += '\n' preamble += '#if !defined(PFN_GetPhysicalDeviceProcAddr)\n' preamble += 'typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName);\n' preamble += '#endif\n' @@ -287,7 +295,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): file_data += '// clang-format on' - write(file_data, file=self.outFile); + write(file_data, file=self.outFile) # Finish processing in superclass OutputGenerator.endFile(self) @@ -341,16 +349,16 @@ class LoaderExtensionOutputGenerator(OutputGenerator): # Retrieve the value of the len tag def getLen(self, param): result = None - len = param.attrib.get('len') - if len and len != 'null-terminated': + length = param.attrib.get('len') + if length and length != 'null-terminated': # For string arrays, 'len' can look like 'count,null-terminated', # indicating that we have a null terminated array of strings. We # strip the null-terminated from the 'len' field and only return # the parameter specifying the string count - if 'null-terminated' in len: - result = len.split(',')[0] + if 'null-terminated' in length: + result = length.split(',')[0] else: - result = len + result = length result = str(result).replace('::', '->') return result @@ -381,11 +389,11 @@ class LoaderExtensionOutputGenerator(OutputGenerator): return_type = cmdinfo.elem.find('proto/type') if (return_type is not None and return_type.text == 'void'): - return_type = None + return_type = None require = None if name == 'vkGetDeviceGroupSurfacePresentModes2EXT': - require_node = self.registry.tree.find("./extensions/extension[@name='{}']/require/command[@name='{}']/..".format(extension_name, name)) + require_node = self.registry.tree.find(f"./extensions/extension[@name='{extension_name}']/require/command[@name='{name}']/..") if 'depends' in require_node.attrib: require = require_node.attrib['depends'] @@ -396,10 +404,10 @@ class LoaderExtensionOutputGenerator(OutputGenerator): params = cmdinfo.elem.findall('param') lens = set() for param in params: - len = self.getLen(param) - if len: - lens.add(len) - paramsInfo = [] + length = self.getLen(param) + if length: + lens.add(length) + for param in params: paramInfo = self.getTypeNameTuple(param) param_type = paramInfo[0] @@ -466,14 +474,24 @@ class LoaderExtensionOutputGenerator(OutputGenerator): # # Retrieve the type and name for a parameter def getTypeNameTuple(self, param): - type = '' - name = '' + t = '' + n = '' for elem in param: if elem.tag == 'type': - type = noneStr(elem.text) + t = noneStr(elem.text) elif elem.tag == 'name': - name = noneStr(elem.text) - return (type, name) + n = noneStr(elem.text) + return (t, n) + + # Convert an XML dependency expression to a C expression, taking a callback to replace extension names + # See https://registry.khronos.org/vulkan/specs/1.4/registry.html#depends-expressions + @staticmethod + def ConvertDependencyExpression(expr, replace_func): + # '(' and ')' can pass through unchanged + expr = re.sub(',', ' || ', expr) + expr = re.sub(r'\+', ' && ', expr) + expr = re.sub(r'\w+', lambda match: replace_func(match.group()), expr) + return expr def OutputPrototypesInHeader(self): protos = '' @@ -543,7 +561,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): protos += 'VKAPI_ATTR VkResult VKAPI_CALL vkDevExtError(VkDevice dev) {\n' protos += ' struct loader_device *found_dev;\n' protos += ' // The device going in is a trampoline device\n' - protos += ' struct loader_icd_term *icd_term = loader_get_icd_and_device(dev, &found_dev, NULL);\n' + protos += ' struct loader_icd_term *icd_term = loader_get_icd_and_device(dev, &found_dev);\n' protos += '\n' protos += ' if (icd_term)\n' protos += ' loader_log(icd_term->this_instance, VULKAN_LOADER_ERROR_BIT, 0,\n' @@ -581,21 +599,21 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if cur_cmd.ext_name != cur_extension_name: if version: - table += '\n // ---- Core %s commands\n' % version.name + table += f'\n // ---- Core {version.name} commands\n' else: - table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + table += f'\n // ---- {cur_cmd.ext_name} extension commands\n' cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: - table += '#if defined(%s)\n' % cur_cmd.protect + table += f'#if defined({cur_cmd.protect})\n' - table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) + table += f' PFN_{cur_cmd.name} {base_name};\n' if cur_cmd.protect is not None: - table += '#endif // %s\n' % cur_cmd.protect + table += f'#endif // {cur_cmd.protect}\n' table += '} VkLayerInstanceDispatchTable;\n\n' return table @@ -625,21 +643,20 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if cur_cmd.ext_name != cur_extension_name: if version: - table += '\n // ---- Core %s commands\n' % version.name + table += f'\n // ---- Core {version.name} commands\n' else: - table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + table += f'\n // ---- {cur_cmd.ext_name} extension commands\n' cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: - table += '#if defined(%s)\n' % cur_cmd.protect - - table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) + table += f'#if defined({cur_cmd.protect})\n' + table += f' PFN_{cur_cmd.name} {base_name};\n' if cur_cmd.protect is not None: - table += '#endif // %s\n' % cur_cmd.protect + table += f'#endif // {cur_cmd.protect}\n' table += '} VkLayerDispatchTable;\n\n' return table @@ -672,24 +689,24 @@ class LoaderExtensionOutputGenerator(OutputGenerator): for cur_cmd in commands: version = self.getAPIVersion(cur_cmd.ext_name) - if (self.ShouldPrintInIcdDispatchTable(cur_cmd, skip_commands)): + if self.ShouldPrintInIcdDispatchTable(cur_cmd, skip_commands): if cur_cmd.ext_name != cur_extension_name: if version: - table += '\n // ---- Core %s commands\n' % version.name + table += f'\n // ---- Core {version.name} commands\n' else: - table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + table += f'\n // ---- {cur_cmd.ext_name} extension commands\n' cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: - table += '#if defined(%s)\n' % cur_cmd.protect + table += f'#if defined({cur_cmd.protect})\n' - table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) + table += f' PFN_{cur_cmd.name} {base_name};\n' if cur_cmd.protect is not None: - table += '#endif // %s\n' % cur_cmd.protect + table += f'#endif // {cur_cmd.protect}\n' table += '};\n\n' return table @@ -735,14 +752,14 @@ class LoaderExtensionOutputGenerator(OutputGenerator): required = False for cur_cmd in commands: version = self.getAPIVersion(cur_cmd.ext_name) - if (self.ShouldPrintInIcdDispatchTable(cur_cmd, skip_gipa_commands)): + if self.ShouldPrintInIcdDispatchTable(cur_cmd, skip_gipa_commands): if cur_cmd.ext_name != cur_extension_name: if version: - table += '\n // ---- Core %s\n' % version.name + table += f'\n // ---- Core {version.name}\n' required = version.number == '1.0' else: - table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + table += f'\n // ---- {cur_cmd.ext_name} extension commands\n' required = False cur_extension_name = cur_cmd.ext_name @@ -750,7 +767,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: - table += '#if defined(%s)\n' % cur_cmd.protect + table += f'#if defined({cur_cmd.protect})\n' if required: # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_# @@ -759,7 +776,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): else: table += f' LOOKUP_GIPA({base_name});\n' if cur_cmd.protect is not None: - table += '#endif // %s\n' % cur_cmd.protect + table += f'#endif // {cur_cmd.protect}\n' table += '\n' table += '#undef LOOKUP_REQUIRED_GIPA\n' @@ -781,7 +798,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): ext.type == 'device' or ext.num_commands == 0): continue - union += ' uint8_t %s;\n' % ext.name[3:].lower() + union += f' uint8_t {ext.name[3:].lower()};\n' union += '};\n\n' return union @@ -800,16 +817,22 @@ class LoaderExtensionOutputGenerator(OutputGenerator): mod_string = new_terminator.replace("VKAPI_CALL vk", "VKAPI_CALL terminator_") if cur_cmd.name in PRE_INSTANCE_FUNCTIONS: + pre_instance_basic_version = mod_string + mod_string = mod_string.replace("terminator_", "terminator_pre_instance_") mod_string = mod_string.replace(cur_cmd.name[2:] + '(\n', cur_cmd.name[2:] + '(\n const Vk' + cur_cmd.name[2:] + 'Chain* chain,\n') - if (cur_cmd.protect is not None): - terminators += '#if defined(%s)\n' % cur_cmd.protect + if cur_cmd.protect is not None: + terminators += f'#if defined({cur_cmd.protect})\n' + + if cur_cmd.name in PRE_INSTANCE_FUNCTIONS: + terminators += pre_instance_basic_version + terminators += '\n' terminators += mod_string terminators += '\n' - if (cur_cmd.protect is not None): - terminators += '#endif // %s\n' % cur_cmd.protect + if cur_cmd.protect is not None: + terminators += f'#endif // {cur_cmd.protect}\n' terminators += '\n' return terminators @@ -874,9 +897,9 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if ((cur_type == 'instance' and is_inst_handle_type) or (cur_type == 'device' and not is_inst_handle_type)): if cur_cmd.ext_name != cur_extension_name: if version: - tables += '\n // ---- Core %s commands\n' % version.name + tables += f'\n // ---- Core {version.name} commands\n' else: - tables += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + tables += f'\n // ---- {cur_cmd.ext_name} extension commands\n' cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name @@ -890,7 +913,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): continue if cur_cmd.protect is not None: - tables += '#if defined(%s)\n' % cur_cmd.protect + tables += f'#if defined({cur_cmd.protect})\n' # If we're looking for the proc we are passing in, just point the table to it. This fixes the issue where # a layer overrides the function name for the loader. @@ -898,18 +921,18 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if base_name == 'GetDeviceProcAddr': tables += ' table->GetDeviceProcAddr = gdpa;\n' elif cur_cmd.ext_type == 'instance': - tables += ' table->%s = (PFN_%s)gipa(inst, "%s");\n' % (base_name, cur_cmd.name, cur_cmd.name) + tables += f' table->{base_name} = (PFN_{cur_cmd.name})gipa(inst, "{cur_cmd.name}");\n' else: - tables += ' table->%s = (PFN_%s)gdpa(dev, "%s");\n' % (base_name, cur_cmd.name, cur_cmd.name) + tables += f' table->{base_name} = (PFN_{cur_cmd.name})gdpa(dev, "{cur_cmd.name}");\n' elif (x < 1 and base_name == 'GetDeviceProcAddr'): tables += ' table->GetDeviceProcAddr = gpa;\n' elif (x > 1 and base_name == 'GetInstanceProcAddr'): tables += ' table->GetInstanceProcAddr = gpa;\n' else: - tables += ' table->%s = (PFN_%s)gpa(%s, "%s");\n' % (base_name, cur_cmd.name, gpa_param, cur_cmd.name) + tables += f' table->{base_name} = (PFN_{cur_cmd.name})gpa({gpa_param}, "{cur_cmd.name}");\n' if cur_cmd.protect is not None: - tables += '#endif // %s\n' % cur_cmd.protect + tables += f'#endif // {cur_cmd.protect}\n' tables += '}\n\n' return tables @@ -967,12 +990,12 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if ((cur_type == 'instance' and is_inst_handle_type) or (cur_type == 'device' and not is_inst_handle_type)): if cur_cmd.ext_name != cur_extension_name: if version: - tables += '\n // ---- Core %s commands\n' % version.name + tables += f'\n // ---- Core {version.name} commands\n' if cur_type == 'device': version_check = f' if (dev->should_ignore_device_commands_from_newer_version && api_version < {version.constant}) return NULL;\n' else: - tables += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + tables += f'\n // ---- {cur_cmd.ext_name} extension commands\n' version_check = '' cur_extension_name = cur_cmd.ext_name @@ -986,7 +1009,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): continue if cur_cmd.protect is not None: - tables += '#if defined(%s)\n' % cur_cmd.protect + tables += f'#if defined({cur_cmd.protect})\n' tables += f' if (!strcmp(name, "{base_name}")) ' if cur_cmd.name in DEVICE_CMDS_MUST_USE_TRAMP: @@ -1002,7 +1025,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): tables += f'return (void *)table->{base_name};\n' if cur_cmd.protect is not None: - tables += '#endif // %s\n' % cur_cmd.protect + tables += f'#endif // {cur_cmd.protect}\n' tables += '\n' tables += ' *found_name = false;\n' @@ -1013,7 +1036,6 @@ class LoaderExtensionOutputGenerator(OutputGenerator): # # Create the appropriate trampoline (and possibly terminator) functions def CreateTrampTermFuncs(self): - entries = [] funcs = '' cur_extension_name = '' @@ -1055,13 +1077,13 @@ class LoaderExtensionOutputGenerator(OutputGenerator): version = self.getAPIVersion(ext_cmd.ext_name) if ext_cmd.ext_name != cur_extension_name: if version: - funcs += '\n// ---- Core %s trampoline/terminators\n\n' % version.name + funcs += f'\n// ---- Core {version.name} trampoline/terminators\n\n' else: - funcs += '\n// ---- %s extension trampoline/terminators\n\n' % ext_cmd.ext_name + funcs += f'\n// ---- {ext_cmd.ext_name} extension trampoline/terminators\n\n' cur_extension_name = ext_cmd.ext_name if ext_cmd.protect is not None: - funcs += '#if defined(%s)\n' % ext_cmd.protect + funcs += f'#if defined({ext_cmd.protect})\n' func_header = ext_cmd.cdecl.replace(";", " {\n") tramp_header = func_header.replace("VKAPI_CALL vk", "VKAPI_CALL ") @@ -1088,14 +1110,14 @@ class LoaderExtensionOutputGenerator(OutputGenerator): requires_terminator = 1 always_use_param_name = False surface_type_to_replace = 'VkSurfaceKHR' - surface_name_replacement = 'icd_surface->real_icd_surfaces[icd_index]' + surface_name_replacement = 'icd_term->surface_list[icd_surface->surface_index]' if param.type == 'VkPhysicalDeviceSurfaceInfo2KHR': has_surface = 1 surface_var_name = param.name + '->surface' requires_terminator = 1 update_structure_surface = 1 update_structure_string = ' VkPhysicalDeviceSurfaceInfo2KHR info_copy = *pSurfaceInfo;\n' - update_structure_string += ' info_copy.surface = icd_surface->real_icd_surfaces[icd_index];\n' + update_structure_string += ' info_copy.surface = icd_term->surface_list[icd_surface->surface_index];\n' always_use_param_name = False surface_type_to_replace = 'VkPhysicalDeviceSurfaceInfo2KHR' surface_name_replacement = '&info_copy' @@ -1109,7 +1131,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): requires_terminator = 1 instance_var_name = param.name - if (ext_cmd.return_type is not None): + if ext_cmd.return_type is not None: return_prefix += 'return ' has_return_type = True @@ -1125,20 +1147,20 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if ext_cmd.handle_type == 'VkPhysicalDevice': funcs += ' const VkLayerInstanceDispatchTable *disp;\n' - funcs += ' VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device(%s);\n' % (phys_dev_var_name) + funcs += f' VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device({phys_dev_var_name});\n' funcs += ' if (VK_NULL_HANDLE == unwrapped_phys_dev) {\n' funcs += ' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' - funcs += ' "%s: Invalid %s "\n' % (ext_cmd.name, phys_dev_var_name) - funcs += ' "[VUID-%s-%s-parameter]");\n' % (ext_cmd.name, phys_dev_var_name) + funcs += f' "{ext_cmd.name}: Invalid {phys_dev_var_name} "\n' + funcs += f' "[VUID-{ext_cmd.name}-{phys_dev_var_name}-parameter]");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' - funcs += ' disp = loader_get_instance_layer_dispatch(%s);\n' % (phys_dev_var_name) + funcs += f' disp = loader_get_instance_layer_dispatch({phys_dev_var_name});\n' elif ext_cmd.handle_type == 'VkInstance': - funcs += ' struct loader_instance *inst = loader_get_instance(%s);\n' % (instance_var_name) + funcs += f' struct loader_instance *inst = loader_get_instance({instance_var_name});\n' funcs += ' if (NULL == inst) {\n' funcs += ' loader_log(\n' funcs += ' NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' - funcs += ' "%s: Invalid instance [VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.name, instance_var_name) + funcs += f' "{ext_cmd.name}: Invalid instance [VUID-{ext_cmd.name}-{instance_var_name}-parameter]");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += '#error("Not implemented. Likely needs to be manually generated!");\n' @@ -1148,8 +1170,8 @@ class LoaderExtensionOutputGenerator(OutputGenerator): funcs += ');\n' funcs += ' if (NULL == disp) {\n' funcs += ' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' - funcs += ' "%s: Invalid %s "\n' % (ext_cmd.name, ext_cmd.params[0].name) - funcs += ' "[VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.params[0].name) + funcs += f' "{ext_cmd.name}: Invalid {ext_cmd.params[0].name} "\n' + funcs += f' "[VUID-{ext_cmd.name}-{ext_cmd.params[0].name}-parameter]");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' @@ -1219,9 +1241,9 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if param.type == 'VkPhysicalDevice': funcs += 'unwrapped_phys_dev' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pNameInfo': - funcs += '&local_name_info' + funcs += '&local_name_info' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pTagInfo': - funcs += '&local_tag_info' + funcs += '&local_tag_info' else: funcs += param.name @@ -1236,7 +1258,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): funcs += term_header if ext_cmd.handle_type == 'VkPhysicalDevice': - funcs += ' struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)%s;\n' % (phys_dev_var_name) + funcs += f' struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *){phys_dev_var_name};\n' funcs += ' struct loader_icd_term *icd_term = phys_dev_term->this_icd_term;\n' funcs += ' if (NULL == icd_term->dispatch.' funcs += base_name @@ -1256,9 +1278,11 @@ class LoaderExtensionOutputGenerator(OutputGenerator): funcs += ' }\n' if has_surface == 1: - funcs += ' VkIcdSurface *icd_surface = (VkIcdSurface *)(%s);\n' % (surface_var_name) - funcs += ' uint8_t icd_index = phys_dev_term->icd_index;\n' - funcs += ' if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)icd_surface->real_icd_surfaces[icd_index]) {\n' + funcs += ' VkIcdSurface *icd_surface = NULL;\n' + funcs += f' if (NULL != {surface_var_name}) {{\n' + funcs += f' icd_surface = (VkIcdSurface *)(uintptr_t)({surface_var_name});\n' + funcs += ' }\n' + funcs += ' if (NULL != icd_surface && NULL != icd_term->surface_list.list && icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) && icd_term->surface_list[icd_surface->surface_index]) {\n' # If there's a structure with a surface, we need to update its internals with the correct surface for the ICD if update_structure_surface == 1: @@ -1307,11 +1331,11 @@ class LoaderExtensionOutputGenerator(OutputGenerator): elif ext_cmd.handle_type == 'VkInstance': - funcs += ' struct loader_instance *inst = loader_get_instance(%s);\n' % (instance_var_name) + funcs += f' struct loader_instance *inst = loader_get_instance({instance_var_name});\n' funcs += ' if (NULL == inst) {\n' funcs += ' loader_log(\n' funcs += ' NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' - funcs += ' "%s: Invalid instance [VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.name, instance_var_name) + funcs += f' "{ext_cmd.name}: Invalid instance [VUID-{ext_cmd.name}-{instance_var_name}-parameter]");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += '#error("Not implemented. Likely needs to be manually generated!");\n' @@ -1325,10 +1349,9 @@ class LoaderExtensionOutputGenerator(OutputGenerator): phys_dev_check = 'VK_OBJECT_TYPE_PHYSICAL_DEVICE' if is_debug_utils else 'VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT' surf_check = 'VK_OBJECT_TYPE_SURFACE_KHR' if is_debug_utils else 'VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT' inst_check = 'VK_OBJECT_TYPE_INSTANCE' if is_debug_utils else 'VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT' - funcs += ' uint32_t icd_index = 0;\n' funcs += ' struct loader_device *dev;\n' - funcs += f' struct loader_icd_term *icd_term = loader_get_icd_and_device({ ext_cmd.params[0].name}, &dev, &icd_index);\n' - funcs += f' if (NULL == icd_term || NULL == dev) {{\n' + funcs += f' struct loader_icd_term *icd_term = loader_get_icd_and_device({ ext_cmd.params[0].name}, &dev);\n' + funcs += ' if (NULL == icd_term || NULL == dev) {\n' funcs += f' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "{ext_cmd.name[2:]}: Invalid device handle");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' @@ -1342,8 +1365,9 @@ class LoaderExtensionOutputGenerator(OutputGenerator): funcs += f' }} else if ({debug_struct_name}->objectType == {surf_check}) {{\n' funcs += ' if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n' funcs += f' VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t){debug_struct_name}->{member_name};\n' - funcs += ' if (NULL != icd_surface->real_icd_surfaces) {\n' - funcs += f' {local_struct}.{member_name} = (uint64_t)icd_surface->real_icd_surfaces[icd_index];\n' + funcs += ' if (NULL != icd_term->surface_list.list && icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR)\n' + funcs += ' && icd_term->surface_list.list[icd_surface->surface_index]) {\n' + funcs += f' {local_struct}.{member_name} = (uint64_t)icd_term->surface_list.list[icd_surface->surface_index];\n' funcs += ' }\n' funcs += ' }\n' funcs += ' // If this is an instance we have to replace it with the proper one for the next call.\n' @@ -1356,7 +1380,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): dispatch = 'dev->loader_dispatch.' else: funcs += f' struct loader_dev_dispatch_table *dispatch_table = loader_get_dev_dispatch({ext_cmd.params[0].name});\n' - funcs += f' if (NULL == dispatch_table) {{\n' + funcs += ' if (NULL == dispatch_table) {\n' funcs += f' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "{ext_cmd.ext_name}: Invalid device handle");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' @@ -1375,7 +1399,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if param.type == 'VkPhysicalDevice': funcs += 'phys_dev_term->phys_dev' elif param.type == 'VkSurfaceKHR': - funcs += 'icd_surface->real_icd_surfaces[icd_index]' + funcs += 'icd_term->surface_list[icd_surface->surface_index]' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pNameInfo': funcs += '&local_name_info' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pTagInfo': @@ -1398,8 +1422,8 @@ class LoaderExtensionOutputGenerator(OutputGenerator): funcs += ');\n' funcs += ' if (NULL == disp) {\n' funcs += ' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' - funcs += ' "%s: Invalid %s "\n' % (ext_cmd.name, ext_cmd.params[0].name) - funcs += ' "[VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.params[0].name) + funcs += f' "{ext_cmd.name}: Invalid {ext_cmd.params[0].name} "\n' + funcs += f' "[VUID-{ext_cmd.name}-{ext_cmd.params[0].name}-parameter]");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' @@ -1425,7 +1449,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): funcs += '}\n\n' if ext_cmd.protect is not None: - funcs += '#endif // %s\n' % ext_cmd.protect + funcs += f'#endif // {ext_cmd.protect}\n' return funcs @@ -1433,7 +1457,6 @@ class LoaderExtensionOutputGenerator(OutputGenerator): # # Create a function for the extension GPA call def InstExtensionGPA(self): - entries = [] gpa_func = '' cur_extension_name = '' @@ -1449,32 +1472,32 @@ class LoaderExtensionOutputGenerator(OutputGenerator): continue if cur_cmd.ext_name != cur_extension_name: - gpa_func += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + gpa_func += f'\n // ---- {cur_cmd.ext_name} extension commands\n' cur_extension_name = cur_cmd.ext_name if cur_cmd.protect is not None: - gpa_func += '#if defined(%s)\n' % cur_cmd.protect + gpa_func += f'#if defined({cur_cmd.protect})\n' #base_name = cur_cmd.name[2:] base_name = SHARED_ALIASES[cur_cmd.name] if cur_cmd.name in SHARED_ALIASES else cur_cmd.name[2:] - if (cur_cmd.ext_type == 'instance'): - gpa_func += ' if (!strcmp("%s", name)) {\n' % (cur_cmd.name) + if cur_cmd.ext_type == 'instance': + gpa_func += f' if (!strcmp("{cur_cmd.name}", name)) {{\n' gpa_func += ' *addr = (ptr_instance->enabled_known_extensions.' gpa_func += cur_cmd.ext_name[3:].lower() gpa_func += ' == 1)\n' - gpa_func += ' ? (void *)%s\n' % (base_name) + gpa_func += f' ? (void *){base_name}\n' gpa_func += ' : NULL;\n' gpa_func += ' return true;\n' gpa_func += ' }\n' else: - gpa_func += ' if (!strcmp("%s", name)) {\n' % (cur_cmd.name) - gpa_func += ' *addr = (void *)%s;\n' % (base_name) + gpa_func += f' if (!strcmp("{cur_cmd.name}", name)) {{\n' + gpa_func += f' *addr = (void *){base_name};\n' gpa_func += ' return true;\n' gpa_func += ' }\n' if cur_cmd.protect is not None: - gpa_func += '#endif // %s\n' % cur_cmd.protect + gpa_func += f'#endif // {cur_cmd.protect}\n' gpa_func += ' return false;\n' gpa_func += '}\n\n' @@ -1500,11 +1523,11 @@ class LoaderExtensionOutputGenerator(OutputGenerator): continue if ext.name != cur_extension_name: - create_func += '\n // ---- %s extension commands\n' % ext.name + create_func += f'\n // ---- {ext.name} extension commands\n' cur_extension_name = ext.name if ext.protect is not None: - create_func += '#if defined(%s)\n' % ext.protect + create_func += f'#if defined({ext.protect})\n' if count == 0: create_func += ' if (0 == strcmp(pCreateInfo->ppEnabledExtensionNames[i], ' else: @@ -1516,7 +1539,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator): create_func += ' = 1;\n' if ext.protect is not None: - create_func += '#endif // %s\n' % ext.protect + create_func += f'#endif // {ext.protect}\n' count += 1 create_func += ' }\n' @@ -1551,21 +1574,22 @@ class LoaderExtensionOutputGenerator(OutputGenerator): last_protect = ext_cmd.protect if ext_cmd.protect is not None: term_func += f'#if defined({ext_cmd.protect})\n' - if (last_ext != ext_cmd.ext_name): + if last_ext != ext_cmd.ext_name: term_func += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name term_func += f' if (!strcmp(name, "{ext_cmd.name[2:]}")) {{\n' - term_func += f' *found_name = true;\n' + term_func += ' *found_name = true;\n' if ext_cmd.require: - term_func += f' return dev->driver_extensions.{ext_cmd.ext_name[3:].lower()}_enabled && dev->driver_extensions.{ext_cmd.require[3:].lower()}_enabled ?\n' + dep_expr = self.ConvertDependencyExpression(ext_cmd.require, lambda ext_name: f'dev->driver_extensions.{ext_name[3:].lower()}_enabled') + term_func += f' return (dev->driver_extensions.{ext_cmd.ext_name[3:].lower()}_enabled && ({dep_expr})) ?\n' else: term_func += f' return dev->driver_extensions.{ext_cmd.ext_name[3:].lower()}_enabled ?\n' term_func += f' (PFN_vkVoidFunction)terminator_{(ext_cmd.name[2:])} : NULL;\n' - term_func += f' }}\n' + term_func += ' }\n' if last_protect is not None: - term_func += '#endif // %s\n' % last_protect + term_func += f'#endif // {last_protect}\n' term_func += ' return NULL;\n' term_func += '}\n\n' @@ -1591,14 +1615,14 @@ class LoaderExtensionOutputGenerator(OutputGenerator): last_protect = ext_cmd.protect if ext_cmd.protect is not None: term_func += f'#if defined({ext_cmd.protect})\n' - if (last_ext != ext_cmd.ext_name): + if last_ext != ext_cmd.ext_name: term_func += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name term_func += f' PFN_{ext_cmd.name} {ext_cmd.name[2:]};\n' if last_protect is not None: - term_func += '#endif // %s\n' % last_protect + term_func += f'#endif // {last_protect}\n' term_func += '};\n\n' @@ -1619,14 +1643,14 @@ class LoaderExtensionOutputGenerator(OutputGenerator): last_protect = ext_cmd.protect if ext_cmd.protect is not None: tramp_protos += f'#if defined({ext_cmd.protect})\n' - if (last_ext != ext_cmd.ext_name): + if last_ext != ext_cmd.ext_name: tramp_protos += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name tramp_protos += f'{ext_cmd.cdecl.replace("VKAPI_CALL vk", "VKAPI_CALL ")}\n' if last_protect is not None: - tramp_protos += '#endif // %s\n' % last_protect + tramp_protos += f'#endif // {last_protect}\n' tramp_protos += '\n' return tramp_protos @@ -1652,20 +1676,21 @@ class LoaderExtensionOutputGenerator(OutputGenerator): last_protect = ext_cmd.protect if ext_cmd.protect is not None: term_func += f'#if defined({ext_cmd.protect})\n' - if (last_ext != ext_cmd.ext_name): + if last_ext != ext_cmd.ext_name: term_func += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name if ext_cmd.require: - term_func += f' if (dev->driver_extensions.{ext_cmd.ext_name[3:].lower()}_enabled && dev->driver_extensions.{ext_cmd.require[3:].lower()}_enabled)\n' + dep_expr = self.ConvertDependencyExpression(ext_cmd.require, lambda ext_name: f'dev->driver_extensions.{ext_name[3:].lower()}_enabled') + term_func += f' if (dev->driver_extensions.{ext_cmd.ext_name[3:].lower()}_enabled && ({dep_expr}))\n' term_func += f' dispatch->{ext_cmd.name[2:]} = (PFN_{(ext_cmd.name)})gpda(dev->icd_device, "{(ext_cmd.name)}");\n' else: term_func += f' if (dev->driver_extensions.{ext_cmd.ext_name[3:].lower()}_enabled)\n' term_func += f' dispatch->{ext_cmd.name[2:]} = (PFN_{(ext_cmd.name)})gpda(dev->icd_device, "{(ext_cmd.name)}");\n' if last_protect is not None: - term_func += '#endif // %s\n' % last_protect + term_func += f'#endif // {last_protect}\n' term_func += '}\n\n' @@ -1695,9 +1720,9 @@ class LoaderExtensionOutputGenerator(OutputGenerator): if cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice': if cur_cmd.ext_name != cur_extension_name: if version: - table += '\n // ---- Core %s commands\n' % version.name + table += f'\n // ---- Core {version.name} commands\n' else: - table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name + table += f'\n // ---- {cur_cmd.ext_name} extension commands\n' cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name @@ -1711,15 +1736,15 @@ class LoaderExtensionOutputGenerator(OutputGenerator): continue if cur_cmd.protect is not None: - table += '#if defined(%s)\n' % cur_cmd.protect + table += f'#if defined({cur_cmd.protect})\n' if base_name == 'GetInstanceProcAddr': - table += ' .%s = %s,\n' % (base_name, cur_cmd.name) + table += f' .{base_name} = {cur_cmd.name},\n' else: - table += ' .%s = terminator_%s,\n' % (base_name, aliased_name) + table += f' .{base_name} = terminator_{aliased_name},\n' if cur_cmd.protect is not None: - table += '#endif // %s\n' % cur_cmd.protect + table += f'#endif // {cur_cmd.protect}\n' table += '};\n\n' return table @@ -1740,12 +1765,11 @@ class LoaderExtensionOutputGenerator(OutputGenerator): continue if ext.protect is not None: - table += '#if defined(%s)\n' % ext.protect + table += f'#if defined({ext.protect})\n' table += ' ' table += ext.define + ',\n' if ext.protect is not None: - table += '#endif // %s\n' % ext.protect + table += f'#endif // {ext.protect}\n' table += ' NULL };\n' return table - diff --git a/scripts/parse_asm_values.py b/scripts/parse_asm_values.py index 87d669326fd2b744a1ed80339794717f99927333..157fb7d5e851658a674397f531c08fd1cd164d34 100644 --- a/scripts/parse_asm_values.py +++ b/scripts/parse_asm_values.py @@ -25,18 +25,20 @@ import sys import os.path from os.path import exists import re +import subprocess +import traceback # Where to write the "gen_defines.asm" file destination_file = sys.argv[1] # The location the build system puts the intermediate asm file which depends on the compiler source_asm_file = sys.argv[2] -# Whether we are using "MASM" or "GAS" for the assembler +# Whether we are using "MASM", "MARMASM" or "GAS" for the assembler assembler_type = sys.argv[3] # Whether we are using gcc, clang, or msvc compiler = sys.argv[4] -# taken from CMAKE_SYSTEM_PROCESSOR - x86_64, aarch64, or x86 -# Only used with GAS - MASM doesn't need this, as it has its own way to determine x86 vs x64 +# taken from CMAKE_SYSTEM_PROCESSOR - x86_64, aarch64|arm64, x86, aarch32|armhf +# Only used with GAS/MARMASM - MASM doesn't need this, as it has its own way to determine x86 vs x64 arch = sys.argv[5] POSIX_COMPILERS = ["GNU", "Clang", "AppleClang"] @@ -57,23 +59,47 @@ defines = ["VULKAN_LOADER_ERROR_BIT", "DISPATCH_OFFSET_ICD_TERM", "EXT_OFFSET_DEVICE_DISPATCH" ] -try: - with open(source_asm_file, 'r') as f: - asm_intermediate_file = f.read() -except IOError: - print("Could not open assembler file:", source_asm_file) - sys.exit(1) +if os.path.splitext(source_asm_file)[1] == ".a": + try: + ar_path = sys.argv[6] + asm_archive_member = sys.argv[7] + subprocess_result = subprocess.Popen([ar_path, "p", source_asm_file, asm_archive_member], stdout=subprocess.PIPE) + asm_intermediate_file = subprocess_result.stdout.read().decode("utf-8") + except IOError: + print("Could not open assembler archive file:", source_asm_file) + traceback.print_exc() + sys.exit(1) +else: + try: + with open(source_asm_file, 'r') as f: + asm_intermediate_file = f.read() + except IOError: + print("Could not open assembler file:", source_asm_file) + sys.exit(1) with open(destination_file, "w", encoding="utf-8") as dest: + # special case vulkan error bit due to it not appearing in the asm - its defined in the header as 8 so it shouldn't change if assembler_type == "MASM": - # special case vulkan error bit due to it not appearing in the asm - its defined in the header as 8 so it shouldn't change dest.write("VULKAN_LOADER_ERROR_BIT equ 8;\n") + elif assembler_type == 'MARMASM': + dest.write(' AREA loader_structs_details, DATA,READONLY\n') + if arch == "aarch64" or arch == "arm64": + dest.write("AARCH_64 EQU 1\n") + elif arch in ["aarch32", "armhf", "arm"]: + dest.write("AARCH_64 EQU 0\n") + else: + print('The parameter "arch" has value of ', arch, ' which is not recognized') + dest.write("VULKAN_LOADER_ERROR_BIT EQU 8\n") elif assembler_type == "GAS": # let the assembler know which platform to use if arch == "x86_64": dest.write(".set X86_64, 1\n") elif arch == "aarch64" or arch == "arm64": dest.write(".set AARCH_64, 1\n") + elif arch in ["aarch32", "armhf", "arm"]: + dest.write(".set AARCH_64, 0\n") + else: + print('The parameter "arch" has value of ', arch, ' which is not recognized') # Nothing to write in the x86 case for d in defines: @@ -81,21 +107,29 @@ with open(destination_file, "w", encoding="utf-8") as dest: if compiler == "MSVC": if d == "VULKAN_LOADER_ERROR_BIT": continue # skip due to special case - match = re.search(d + " DD [ ]*([0-9a-f]+)H", asm_intermediate_file) + if 'arm' in arch.lower(): + match = re.search('\\|'+ d + '\\| DCD[\t ]*0x([0-9a-f]+)', asm_intermediate_file) + else: + match = re.search(d + " DD [ ]*([0-9a-f]+)H", asm_intermediate_file) elif compiler in POSIX_COMPILERS: match = re.search(d + " = ([0-9]+)", asm_intermediate_file) - if match: + if len(match.groups()) > 0: if compiler == "MSVC": value = str(int(match.group(1), 16)) elif compiler in POSIX_COMPILERS: value = match.group(1) - if assembler_type == "MASM": + # MASM uses hex values, decode them here + if assembler_type == "MASM": dest.write(d + " equ " + value +";\n") + elif assembler_type == 'MARMASM': + dest.write(d + ' EQU ' + value +'\n') elif assembler_type == "GAS": dest.write(".set " + d + ", " + value + "\n") else: print("Couldn't find ", d) sys.exit(1) + if assembler_type == 'MARMASM': + dest.write(" END\n") diff --git a/scripts/qnx/common.mk b/scripts/qnx/common.mk index 6c2e50a99d04977e3410189b270511957fbc4eb4..bd10e71eb3696a8428e930c03e45cf6eb7961e65 100644 --- a/scripts/qnx/common.mk +++ b/scripts/qnx/common.mk @@ -32,7 +32,7 @@ include $(MKFILES_ROOT)/qtargets.mk CCFLAGS += -DVK_USE_PLATFORM_SCREEN_QNX=1 -DVK_ENABLE_BETA_EXTENSIONS CCFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -CCFLAGS += -Wno-stringop-truncation +CCFLAGS += -fno-strict-aliasing -Wno-stringop-truncation CCFLAGS += -Wno-stringop-overflow -fvisibility=hidden CCFLAGS += -Wpointer-arith -fPIC diff --git a/scripts/update_deps.py b/scripts/update_deps.py old mode 100755 new mode 100644 index 35b4990d5f19e8b866f8ce8d7696cb87f0408b91..95b8f30570837d677409d0d85bad65506015dea3 --- a/scripts/update_deps.py +++ b/scripts/update_deps.py @@ -295,7 +295,7 @@ def run_cmake_command(cmake_cmd): # NOTE: Because CMake is an exectuable that runs executables # stdout/stderr are mixed together. So this combines the outputs # and prints them properly in case there is a non-zero exit code. - result = subprocess.run(cmake_cmd, + result = subprocess.run(cmake_cmd, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, text = True @@ -415,8 +415,15 @@ class GoodRepo(object): if VERBOSE: print('Checking out {n} in {d}'.format(n=self.name, d=self.repo_dir)) - if self._args.do_clean_repo: + if os.path.exists(os.path.join(self.repo_dir, '.git')): + url_changed = command_output(['git', 'config', '--get', 'remote.origin.url'], self.repo_dir).strip() != self.url + else: + url_changed = False + + if self._args.do_clean_repo or url_changed: if os.path.isdir(self.repo_dir): + if VERBOSE: + print('Clearing directory {d}'.format(d=self.repo_dir)) shutil.rmtree(self.repo_dir, onerror = on_rm_error) if not os.path.exists(os.path.join(self.repo_dir, '.git')): self.Clone() @@ -503,11 +510,14 @@ class GoodRepo(object): # Use the CMake -A option to select the platform architecture # without needing a Visual Studio generator. if platform.system() == 'Windows' and self._args.generator != "Ninja": + cmake_cmd.append('-A') if self._args.arch.lower() == '64' or self._args.arch == 'x64' or self._args.arch == 'win64': - cmake_cmd.append('-A') cmake_cmd.append('x64') + elif self._args.arch == 'arm64': + cmake_cmd.append('arm64') + elif self._args.arch == 'arm': + cmake_cmd.append('arm') else: - cmake_cmd.append('-A') cmake_cmd.append('Win32') # Apply a generator, if one is specified. This can be used to supply @@ -620,7 +630,7 @@ def CreateHelper(args, repos, filename): if repo.api is not None and repo.api != args.api: continue if install_names and repo.name in install_names and repo.on_build_platform: - helper_file.write('set({var} "{dir}" CACHE STRING "" FORCE)\n' + helper_file.write('set({var} "{dir}" CACHE STRING "")\n' .format( var=install_names[repo.name], dir=escape(repo.install_dir))) @@ -683,7 +693,7 @@ def main(): parser.add_argument( '--arch', dest='arch', - choices=['32', '64', 'x86', 'x64', 'win32', 'win64'], + choices=['32', '64', 'x86', 'x64', 'win32', 'win64', 'arm', 'arm64'], type=str.lower, help="Set build files architecture (Visual Studio Generator Only)", default='64') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 10273899b79e039284bf1de37f2bb7a4d3210373..8145f638d376632f68fb44ddeab9ae602ce71127 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,16 +23,10 @@ set(CMAKE_CXX_EXTENSIONS OFF) # Make sure tests uses the dynamic runtime instead set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") -# For MSVC/Windows, replace /GR with an empty string, this prevents warnings of /GR being overriden by /GR- -# Newer CMake versions (3.20) have better solutions for this through policy - using the old -# way while waiting for when updating can occur -string(REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - if (IS_DIRECTORY "${GOOGLETEST_INSTALL_DIR}/googletest") set(BUILD_GTEST ON) set(BUILD_GMOCK OFF) set(gtest_force_shared_crt ON) - set(BUILD_SHARED_LIBS ON) set(INSTALL_GTEST OFF) add_subdirectory("${GOOGLETEST_INSTALL_DIR}" ${CMAKE_CURRENT_BINARY_DIR}/gtest) else() @@ -95,6 +89,14 @@ add_executable( target_link_libraries(test_regression PUBLIC testing_dependencies) target_compile_definitions(test_regression PUBLIC VK_NO_PROTOTYPES) +add_executable( + test_fuzzing + loader_testing_main.cpp + loader_fuzz_tests.cpp +) +target_link_libraries(test_fuzzing PUBLIC testing_dependencies vulkan) +target_include_directories(test_fuzzing PUBLIC ${CMAKE_SOURCE_DIR}/loader ${CMAKE_SOURCE_DIR}/loader/generated) + # Threading tests live in separate executabe just for threading tests as it'll need support # in the test harness to enable in CI, as thread sanitizer doesn't work with address sanitizer enabled. add_executable( @@ -110,19 +112,10 @@ if (ENABLE_LIVE_VERIFICATION_TESTS) endif() if(WIN32) - # Copy loader and googletest (gtest) libs to test dir so the test executable can find them. - add_custom_command(TARGET test_regression POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $) - # Copy the loader shared lib (if built) to the test application directory so the test app finds it. + # Copy vulkan-1.dll to the test_fuzzing build directory so that the test_fuzzing exe can find it. if(TARGET vulkan) - add_custom_command(TARGET test_regression POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $) - endif() - - # Copy the gtest shared lib (if built) to the live verification tests directory so the tests finds it. - if(ENABLE_LIVE_VERIFICATION_TESTS) - add_custom_command(TARGET test_regression POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $) + add_custom_command(TARGET test_fuzzing POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $) endif() endif() @@ -137,8 +130,10 @@ endif() # must happen after the dll's get copied over if(NOT CMAKE_CROSSCOMPILING) gtest_discover_tests(test_regression PROPERTIES DISCOVERY_TIMEOUT 100) + gtest_discover_tests(test_fuzzing PROPERTIES DISCOVERY_TIMEOUT 100) else() gtest_add_tests(TARGET test_regression) + gtest_add_tests(TARGET test_fuzzing) endif() # When APPLE_STATIC_LOADER is ON installation is disabled @@ -169,3 +164,8 @@ add_test(NAME integration.find_package # Installing comes before testing set_tests_properties(integration.find_package PROPERTIES DEPENDS integration.install) + +if (CODE_COVERAGE) + target_code_coverage(test_regression AUTO ALL ) + target_code_coverage(test_fuzzing AUTO ALL ) +endif() diff --git a/tests/framework/CMakeLists.txt b/tests/framework/CMakeLists.txt index e61825ce8a284c0dc276cdcaba8f41595c4fc116..7e34c8ce205ab6964d7784e23ce4f7a8c4eea776 100644 --- a/tests/framework/CMakeLists.txt +++ b/tests/framework/CMakeLists.txt @@ -46,8 +46,10 @@ if (UNIX) endif() if (MSVC) -# silence hidden class member warnings in test framework + # silence hidden class member warnings in test framework target_compile_options(testing_framework_util PUBLIC /wd4458) + # Make sure exception handling is enabled for the test framework + target_compile_options(testing_framework_util PUBLIC /EHsc) endif() function(AddSharedLibrary LIBRARY_NAME) @@ -74,27 +76,16 @@ add_subdirectory(layer) #setup framework_config_temp.h.in in the current binary directory configure_file("${CMAKE_CURRENT_SOURCE_DIR}/framework_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in") -# setup framework_config.h.in using framework_config_temp.h.in as a source +# setup framework_config_$ using framework_config_temp.h.in as a source file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$.h" INPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in") -# copy framework_config_$ to the loader build directory -add_custom_command( - PRE_BUILD - COMMAND ${CMAKE_COMMAND} "-E" "copy_if_different" "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$.h" "${CMAKE_CURRENT_BINARY_DIR}/framework_config.h" - VERBATIM - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$.h" - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config.h" - COMMENT "creating framework_config.h file ({event: PRE_BUILD}, {filename: framework_config.h })" - ) -add_custom_target (generate_framework_config DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/framework_config.h") -add_dependencies (generate_framework_config vulkan) -add_dependencies (testing_framework_util generate_framework_config) +# Add a compiler definition for the path to framework_config.h with the correct config +target_compile_definitions(testing_framework_util PUBLIC FRAMEWORK_CONFIG_HEADER="framework_config_$.h") add_library(testing_dependencies STATIC test_environment.cpp test_environment.h) target_link_libraries(testing_dependencies PUBLIC gtest Vulkan::Headers testing_framework_util shim-library) target_include_directories(testing_dependencies PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) -target_compile_definitions(testing_dependencies PUBLIC "GTEST_LINKED_AS_SHARED_LIBRARY=1") if (APPLE_STATIC_LOADER) target_compile_definitions(testing_dependencies PUBLIC "APPLE_STATIC_LOADER=1") target_link_libraries(testing_dependencies PUBLIC vulkan) diff --git a/tests/framework/README.md b/tests/framework/README.md index 7ae04c22e714295f4bc99f3ba2a3dc19b1d35e02..1dc5b73a4a19c9d8bb1f270b114ef2157234921d 100644 --- a/tests/framework/README.md +++ b/tests/framework/README.md @@ -136,7 +136,6 @@ There are many utilities that the test framework and tests have access to. These * Environment Variable Wrapper: `EnvVarWrapper` for creating, setting, getting, and removing environment variables in a RAII manner * Windows API error handling helpers * filesystem abstractions: - * `fs::path` - wrapper around std::string that has a similar API to C++17's `filesystem::path` library * `create_folder`/`delete_folder` * `FolderManager` * Creates a new folder with the given name at construction time. diff --git a/tests/framework/data/binaries/dummy_library_elf_32.dll b/tests/framework/data/binaries/dummy_library_elf_32.dll old mode 100755 new mode 100644 diff --git a/tests/framework/data/binaries/dummy_library_elf_64.dll b/tests/framework/data/binaries/dummy_library_elf_64.dll old mode 100755 new mode 100644 diff --git a/tests/framework/data/binaries/libdummy_library_elf_32.so b/tests/framework/data/binaries/libdummy_library_elf_32.so old mode 100755 new mode 100644 diff --git a/tests/framework/data/binaries/libdummy_library_elf_64.so b/tests/framework/data/binaries/libdummy_library_elf_64.so old mode 100755 new mode 100644 diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5599244505186304 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5599244505186304 new file mode 100644 index 0000000000000000000000000000000000000000..a939fe81368af35bb7859e058a1471f63a8d845f Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5599244505186304 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5817896795701248 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5817896795701248 new file mode 100644 index 0000000000000000000000000000000000000000..7098959979bddf1618a094c1cd415d20393a417c Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-5817896795701248 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-6541440380895232 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-6541440380895232 new file mode 100644 index 0000000000000000000000000000000000000000..e748a3c9c24c9fdc879e1d4fe4ef65abd0122044 Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_create_fuzzer-6541440380895232 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-5126563864051712 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-5126563864051712 new file mode 100644 index 0000000000000000000000000000000000000000..6cfe2cda902391bc075399bea3fbb9c8f4c4f3c6 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-5126563864051712 @@ -0,0 +1,3 @@ +{"file_format_version": true, + "settings_array": [ { "layers":[ + {"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"control":"":"unordered_layer_location" },{"control":"unordered_layern" },{"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"control":"unordered_layer_location" },{"contrAl":"unordered_layer_location" },{"control":"unordered_layon" },{"control":"unordered_layer_location" },{"_layer_location" control":"},{"unc \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-6308459683315712 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-6308459683315712 new file mode 100644 index 0000000000000000000000000000000000000000..ef1c9d9b82eb79241f53b3544a6c0f63d231713a --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-instance_enumerate_fuzzer-6308459683315712 @@ -0,0 +1,246 @@ +{ + "file_format_version": "2.0.0", + "layers": [ + { "name": "INSTANCE", + "type": "INSTANCE", + "implementation_version": "1", + "api_version": "0..1-0", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "2.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": [], + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.257.-1", + "implementation_version": "1", + "description": [], + "library_path": "--1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-7", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.3.-2", + "implementation_version": "1", + "description": [], + "library_path": "-2", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "2", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-2", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_kays":[] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-0", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "2", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-2", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environment": { + "test_key": "type" + }, + "app_ke~s": [] }, + { + "name": "INSTANCE", + "type": "INSTANCE", + "api_version": "3.2.-1", + "implementation_version": "1", + "description": [], + "library_path": "-1", + "disable_environmentapp_kays":[] + } + ], + "": { + } +} \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-json_load_fuzzer-5258042868105216 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-json_load_fuzzer-5258042868105216 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-json_load_fuzzer-5487817455960064 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-json_load_fuzzer-5487817455960064 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4558978302214144 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4558978302214144 new file mode 100644 index 0000000000000000000000000000000000000000..5d109881aab2347fc97c0cfcfced9f85b6db136a --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4558978302214144 @@ -0,0 +1,99 @@ +{ + "file_format_version": "1.2.0", + "settings_array": [ + { + "app_keys": [ + "/out/settings_fuzzer", + "/work/settings_fuzzer" + ], + "stderr_log": ["all", "info", "warn", "perf", "error", "debug", "layer", "driver", "validation"], + "log_locations": [ + { + "destinations":["/tmp"], + "filters":["all"] + } + ], + "layers": [ + { + "control": "auto", + "name": "test_auto", + "path": "test", + "treat_as_implicit_manifest": true + }, + { + "control": "on", + "name": "test_on", + "path": "test", + "treat_as_implicit_manifest": true + }, + { + "control": "off", +  "name": "test_off", + "path": "test", + "treat_as_implicit_manifest": true + } + ] +  }, + { + "app_keys": [ + "val2147483650", + "val18446744073709551617", + "val-407736571", + "val18446744073709551615" + ] + } + ], + "layers": [ + { + "name": "VK_LAYER_test_layer_1", + "type": "INSTANCE", + "api_version": "1.340282366920938463463374607431768211458.231", + "implementation_version": "1", + "description": "Test layer 1", + "library_path": "libVkLayer_khronos_validation.so", + "disable_environment": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + }, + { + "name": "VK_LAYER_test_layer_2", + "type": "GLOBAL", + "api_version": "1.3.32536", + "implementation_version": "1", + "description": "Test layer 2", + "library_path": "libVkLayer_khronos_validation.so", + "component_layers": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_340282366920938463463374607431768211457"], + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + }, + { + "name": "VK_LAYER_KHRONO", + "type": "GLOBAL", + "api_version": "1.3.230", + "implementation_version": "1", + "description": "Khronos validation", + "library_path": "libVkLayer_khronos_validation.so", + "component_layers": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + } + ], + "ICD": { + "library_path": "/src/vulkan-loader/tests/framework/data/binaries/libdummy_library_elf_32.so", + "api_version": "1.18446744073709551616.231" + } +} diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4568454561071104 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4568454561071104 new file mode 100644 index 0000000000000000000000000000000000000000..c0ecc53bf8231da720abdb496a95f29cf90d7f00 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4568454561071104 @@ -0,0 +1,6 @@ +{ + "file_format_version":"D", + "settings_array": [ +{ + "stderr_log": ["info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "kl", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "i.fo", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "ifo", "info", "info", "info", "info", "kl", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", 3852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,2,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,2,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317944107,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,3,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,0,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,11,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,365170681636289,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,2147483648,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,7943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,256,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317953852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,43852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,2,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,129,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,-293681531150,1,1,1,1,1,1,1,1,2,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,117943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,65536,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,1,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,15,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,129,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,170141183460469231731687303715884105727,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,65536,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,131,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943853,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,11,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,0,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,101.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,141,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,11,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,65536,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,131,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943853,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.11,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,170141183460469231731687303715884105727,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,0,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,13179438,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,141,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,111,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,2,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,2,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317944107,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,3,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,0,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,11,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,365170681636289,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,2147483648,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,7943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,256,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317953852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,43852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,2,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,129,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,-293681531150,1,1,1,1,1,1,1,1,2,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,117943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,65536,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,1,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,15,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,129,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,170141183460469231731687303715884105727,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,65536,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,131,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943853,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,11,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,11,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,0,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,101.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,2,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,11.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,691694334,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1317943852,1,1,1,11,0,1,2,1,1,1,1,1,1,1,1,1,1,1,1.1,1,1,1,1,1,1,1,1,1,1,1,1,"info", "info", "info", "info", "info", "i.fo", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "ingo", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "nfo", "info", "info", "info", "ingo", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info", "info"] +  } ] } diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4820577276723200 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4820577276723200 new file mode 100644 index 0000000000000000000000000000000000000000..36da6d1a8898eb4890320b3a5399601ddd31f817 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-4820577276723200 @@ -0,0 +1,50 @@ +{ + "file_format_version": "1.2.0", + "settings_array": [ + { + "app_keys": [ + "val1", + "val2", + "val3", + "val4" + ] } + ], +"layers": [ +{ + "name": "VK_LAYER_test_layer_1", + "type": "INSTANCE", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Test layer 1", + "library_path": "", + "compone + rs": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" }, + "enable_environment": { +"test_key": "test_value" }, + "app_keys": ["/out/settings_fuzzer"] }, +{ + "name": "VK_LAYER_KHRONOS_validation", + "type": "INSTANCE", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Test layer 1", + "library_path": "", + "compone + rs": ["VK_LAYER_test_layer_0", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" }, + "enable_environment": { +"test_key": "test_value" }, + "app_keys": ["/out/settings_fuzzer"] }, +{ + + "app_keys": ["/out/settings_fuzzer"] + } + ], + "ICD": { + "library_path": "/src/vulka-loader/te%sts/framework/d`ta/binaries/libdummy_library_elf_30.so", + "api_version": "-170141183460469231731687303715884105729.127.0" + } +} \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5177827962454016 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5177827962454016 new file mode 100644 index 0000000000000000000000000000000000000000..e0d05e84e27897ff04e46ca3e0321407c8c67f93 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5177827962454016 @@ -0,0 +1,7 @@ +{ + "file_format_version": "1.-130057", + "layers": [ + { "type": "INSTANCE", + "name": "3", "api_version":"0", + "implementation_version":"\udFuH\uFetF\uEatG\uFFt"\ucFuG\uFet"\ucFuF\uFet"\ucFu\uFet"\ucFuG\uFet"\ucFuG\uFet"\ucFuG\uFdt"\ucFuG\uFet"\ucFuG\uFet"\ucFuG\uFet"\ucF}G\uFet"\ucFuG\uFet"\ucFuG\uFet"\ucFuG\uFet"\udFuG\uFet"\ucFuG\uFet"\ucFuG\uFdt"\ucFuG\uFet"\uc [], "description":[] }, { "type": "INSTANCE"FuG\uFft \ucEuG\uFet!\ucFuG\uF, + "name": "1et"\ucFuG\uFet"\ucFuF\uFet"\ucFuG\uFet"\ucFuG\uFet"\ucFuG\uFet"", \ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5198773675425792 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5198773675425792 new file mode 100644 index 0000000000000000000000000000000000000000..9689a9d7bf30703219992ab0fec8edeaf5ec4fdf --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5198773675425792 @@ -0,0 +1 @@ +{ "file_format_version": ["kkl", [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["rf"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]} diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5416197367070720 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5416197367070720 new file mode 100644 index 0000000000000000000000000000000000000000..a3cbcaa1f1cd872bee50fbaf8422796d668f6e89 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5416197367070720 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ "ivmY\uf "ivmY\ufiin[[[0,[[[[[[[[[[[[[0,[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[0,[[[[[[[[[[[[[0,[[sdi[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[0,[[[insd[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5494771615137792 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5494771615137792 new file mode 100644 index 0000000000000000000000000000000000000000..a6d034e56880819f53346503535b8dfba50fc23c --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5494771615137792 @@ -0,0 +1,99 @@ +{ + "file_format_version": "1.2.0", + "settings_array": [ + { + "app_keysw": [ + "/out/settings_fuzzer", + "/work/settings_fuzzer" + ], + "stderr_log": ["all", "info", "warn", "perf", "error", "debug", "layer", "diver", "validation"], + "log_locations": [ + { + "destinations":["/tmp"], + "filters":["all"] + } + ], + "eralys": [ + { + "control": "auto", + "name": "test_auto", + "path": "test", + "treat_as_implicit_manifest": true + }, + { + "control": "on", + "name": "test_on", + "path": "test", + "treat_as_implicit_manifest": true + }, + { + "control": "off", + "name": "test_off", + "path": "test", + "treat_as_implicit_manifest": true + } + ] + }, + { + "app_keys": [ + "val1", + "val2", + "val3", + "val4" + ] + } + ], + "layers": [ + { + "name": "VK_LAYER_test_layer_1", + "type": "INSTANCE", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Test layer 1", + "jibrary_path": "libVkLayer_khronos_validation.so", + "component_layers": ["VK_LAYER_test_layer_2", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + }, + { + "name": "VK_LAYER_test_layer_2", + "type": "GLOBAL", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Test layer 2", + "library_path": "libVkLayer_khronos_validation.so", + "component_layers": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + }, + { + "name": "VK_LAYER_KHRONOS_validation", + "type": "GLOBAL", + "api_version": "1.3.231", + "implementation_version": "1", + "descriptmon": "Khronos validation", + "library_path": "libVkLayer_khronos_validation.so", + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + } + ], + "ICD": { + "library_path": "/src/vulkan-loader/tests/framework/data/binaries/libdummy_library_elf_32.so", + "api_version": "1.3.231" + } +} diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5801855065915392 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5801855065915392 new file mode 100644 index 0000000000000000000000000000000000000000..58448a688789e029434330fa77df8d08f060b9a7 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-5801855065915392 @@ -0,0 +1,88 @@ +{ + "file_format_version": "1.2.0", + "settings_array": [ + { + "app_keys": [ + "val1", + "val2", + "va,3", + "val4" + ] } + ], +"layers": [ +{ + "name": "VK_LAYER_test_layer_1", + "implementation_version": "1", + "description": "Test layer 1", + "library_path": "", + "compone + rs": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" }, + "enable_environlent": { +"test_key": "test_value" }, + "app_keys": ["/out/settings_fuzzer"] }, +{ + "name": "VK_LAYER_KHRONOS_validation", + "type": "INSTANCE", + "api_version": "1.3.231", + "implementation_version": "2", + "description": "Test layer 1", + "library_path": "", + "compone + rs": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test.jsone" }, + "enable_environment": { +"test_key": "test_value" }, + "app_keys": ["/out/settings_fuzzer"] }, +{ + "name": "VK_LAYER_KHRONOS_validation", + "type": "INSTANCE", + "api_version": "0.3.231", + "implementation_version": "1", + "description": "Test layer 1", + "library_path": "", + "comone + rs": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" }, + "ena)ble_environment": { +"test_key": "test_value" }, + "app_keys": ["/out/settings_fuzzer"] }, +{ + "name": "VK_LAYER_KHRONOS_validation", + "type": "INSTANCE", + "api_verPsion": "0.3.231", + "implementation_version": "1", + "description": "Test layer 0", + "library_path": "", + "compone + rs": ["VK_LAYER_test_layer_18446744073709551615", "VK_LAYER_test_layer_2"], + "disable_ejvironment": { + "test_key": "test_value" }, + "enable_environment": { +"test_key": "test_value" }, + "app_keys": ["/out/settings_fuzzer"] }, +{ + "name": "VK_LAYER_KHRONOS_validation", + "type": "INSTANCE", + "api_version": "1.3.231", + "implementation_version": "126", + "description": "Test layer 1", + "library_path": "", + "compone + rs": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_en,ironment": { + "test_key": "test_value" }, + "enable_environment": { +"test_key": "test_value" }, + "+pp_keys": ["/out/settings_fuzzer"] + } + ], + "ICD": { + "library_path": "", + "library_path": "/src/vulka-loader/tstes/framework/d`ta/binaries/libdummy_library_elf_30.so", + "api_version": "-1701411834604692317303715884105729.85.d/..0230743847030624.0" + } +} \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-6353004288081920 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-6353004288081920 new file mode 100644 index 0000000000000000000000000000000000000000..a2ca7bb7f5cf2a26f256b17bc7ebf6b8fd431dc0 Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_create_fuzzer-6353004288081920 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6465902356791296 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6465902356791296 new file mode 100644 index 0000000000000000000000000000000000000000..0590401decb07178821682d7168c599088d3b301 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6465902356791296 @@ -0,0 +1,2 @@ +{"file_format_version" :{"layers":2e170141183460469231731687303715884105986}, "settings_array": [ +{"laㅤyers":18446744073709551615e-1} ] } \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6583684169269248 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6583684169269248 new file mode 100644 index 0000000000000000000000000000000000000000..474b8d3f98dbebb533df34147d3c9160990d02d2 Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6583684169269248 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4512865114259456 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4512865114259456 new file mode 100644 index 0000000000000000000000000000000000000000..d6095e0f1a4fd4ac8668b74718d1b2ece0b93b81 Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4512865114259456 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 new file mode 100644 index 0000000000000000000000000000000000000000..f20d4ba7f72649d6b45efac0fd8a0015db33b418 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768 @@ -0,0 +1 @@ +[18446744073709551616e-39065906811251e \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 new file mode 100644 index 0000000000000000000000000000000000000000..5167c9f19a6edca339d402fa396ee8fb1d138db8 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 new file mode 100644 index 0000000000000000000000000000000000000000..810c539bc71873d023c04212b53a0b3ad91f6814 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992 @@ -0,0 +1 @@ +[3] \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 new file mode 100644 index 0000000000000000000000000000000000000000..e2cb11d64a03fece74de850aa26ea4344c029181 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["\u"\\DB1E\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[uD0f[[[B[[[[[[[[[[[[[[[[2[[[[[[[[[[[[[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 new file mode 100644 index 0000000000000000000000000000000000000000..3c77fe4be66b5f14df5bbe01f2cff8c5d81df2a3 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["\udf" [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 new file mode 100644 index 0000000000000000000000000000000000000000..08cf6bdf0ab5ea9be55c648c24ebccfa49a2293c --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536 @@ -0,0 +1,2 @@ +{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":9223372036854775808,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":9223372036854775808,"{":{"f":1,"{":{"f":1,"{":{"f":0,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":2147483649,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":214748364,"{":{"f":1,"{":{"f":1,"{":{"f":-54,"{":{"f":1,"{{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":2147483649,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{{":{"f":2,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f":1,"{":{"f{ +"4":2147483649,"{"0,"":4,4,":{"f":"f":1,"{":{"f \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928 new file mode 100644 index 0000000000000000000000000000000000000000..91aea359d6bab3a2e1dde8a1ae4c8148ecd0c616 Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584 new file mode 100644 index 0000000000000000000000000000000000000000..253b08499a7a8c0e384328b2af061e3bac65b635 Binary files /dev/null and b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584 differ diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6482033715838976 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6482033715838976 new file mode 100644 index 0000000000000000000000000000000000000000..87e297a006f5f02886b63ee48a082aa05e0fde8d --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-json_load_fuzzer-6482033715838976 @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-4857714377818112 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-4857714377818112 new file mode 100644 index 0000000000000000000000000000000000000000..a2aa2814fffb7a47225aec74b7c87b4035759cc1 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-4857714377818112 @@ -0,0 +1,2 @@ +{"file_format_version":[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["\u4]\"\u3{ +"file_format_version[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Y[[[[[[[": "","settings_ar[ray": [{"stderr_log":$\ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["","va[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[lElda[[[["][[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[}][[[[[[[[[[[[[[[[[[[[}[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file diff --git a/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-5123849246867456 b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-5123849246867456 new file mode 100644 index 0000000000000000000000000000000000000000..d018a1514441f2dbb51743ed343926d9ba80b7c3 --- /dev/null +++ b/tests/framework/data/fuzz_test_minimized_test_cases/clusterfuzz-testcase-minimized-settings_fuzzer-5123849246867456 @@ -0,0 +1,99 @@ +{ + "file_format_version": "1.2.0", + "settings_array": [ + { + "app_keys": [ + "/out/settings_fuzzer", + "/work/settings_fuzzer" + ], + "stderr_log": ["all", "info", "warn", "perf", "error", "debug", "layer", "driver", "validation"], + "log_locations": [ + { + "destinations":["/tmp"], + "filters":["all"] + } + ], + "layers": [ + { + "control": "auto", + "name": "test_auto", + "path": "test", + "treat_as_implicit_manifest": true + }, + { + "control": "on", + "name": "test_on", + "path": "test", + "treat_as_implicit_manifest": true + }, + { + "control": "off", + "name": "test_off", + "path": "test", + "treat_as_implicit_manifest": true + } + ] + }, + { + "app_keys": [ + "val1", + "val2", + "val3", + "val4" + ] + } + ], + "layers": [ + { + "name": "VK_LAYER_test_layer_1", + "type": "INSTANCE", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Test layer 1", + "library_path": "libVkLayer_khronos_validation.so", + "component_layers": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + }, + { + "name": "VK_LAYER_test_layer_2", + "type": "GLOBAL", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Test layer 2", + "library_path": "libVkLayer_khronos_validation.so", + "component_layers": ["VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"], + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_:value" + }, + "app_keys": ["/out/settings_fuzzer"] + }, + { + "name": "VK_LAYER_KHRONOS_validation", + "type": "GLOBAL", + "api_version": "1.3.231", + "implementation_version": "1", + "description": "Khronos validation", + "library_path": "libVkLayer_khronos_validation.so", + "disable_environment": { + "test_key": "test_value" + }, + "enable_environment": { + "test_key": "test_value" + }, + "app_keys": ["/out/settings_fuzzer"] + } + ], + "ICD": { + "library_path": "/src/vulkan-loader/tests/framework/data/binaries/libdummy_library_elf_32.so", + "api_version": "1.3.231" + } +} diff --git a/tests/framework/framework_config.h.in b/tests/framework/framework_config.h.in index aa14908d2853bcb23f350d246707b46236509287..d8d21b98c769fcc2e261caf236c8a262e33bbd01 100644 --- a/tests/framework/framework_config.h.in +++ b/tests/framework/framework_config.h.in @@ -56,7 +56,7 @@ // Version 7 #define TEST_ICD_PATH_VERSION_7 "$" -#define TEST_ICD_PATH_VERSION_7_WITH_ADDITIONAL_EXPORTS "$" +#define TEST_ICD_PATH_VERSION_7_WIHTOUT_EXPORTS "$" // TestLayer binaries #define TEST_LAYER_PATH_EXPORT_BASE "$" @@ -71,6 +71,8 @@ #define COMPLEX_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/data/VkLayer_complex_file.json" #define FUZZER_OUTPUT_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/data/fuzzer_output.json" +#define CLUSTERFUZZ_TESTCASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/fuzz_test_minimized_test_cases" + // Dummy Binaries #if _WIN32 || _WIN64 #define DUMMY_BINARY_WINDOWS_64 "${CMAKE_CURRENT_SOURCE_DIR}/data/binaries/dummy_library_pe_64.dll" @@ -78,7 +80,7 @@ #define BAD_DUMMY_BINARY_WINDOWS_64 "${CMAKE_CURRENT_SOURCE_DIR}/data/binaries/libdummy_library_elf_64.dll" #define BAD_DUMMY_BINARY_WINDOWS_32 "${CMAKE_CURRENT_SOURCE_DIR}/data/binaries/libdummy_library_elf_32.dll" #endif -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) #define DUMMY_BINARY_LINUX_64 "${CMAKE_CURRENT_SOURCE_DIR}/data/binaries/libdummy_library_elf_64.so" #define DUMMY_BINARY_LINUX_32 "${CMAKE_CURRENT_SOURCE_DIR}/data/binaries/libdummy_library_elf_32.so" #define BAD_DUMMY_BINARY_LINUX_64 "${CMAKE_CURRENT_SOURCE_DIR}/data/binaries/dummy_library_pe_64.so" @@ -96,7 +98,7 @@ #endif #endif #if defined(__linux__) || defined(__GNU__) -#if __x86_64__ || __ppc64__ +#if __x86_64__ || __ppc64__ || __aarch64__ #define CURRENT_PLATFORM_DUMMY_BINARY_WRONG_TYPE DUMMY_BINARY_LINUX_32 #define CURRENT_PLATFORM_DUMMY_BINARY_BAD BAD_DUMMY_BINARY_LINUX_64 #else @@ -105,7 +107,7 @@ #endif #endif // Apple doesn't have 32 bit but this should allow tests to run as if it was supported -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__QNX__) #define CURRENT_PLATFORM_DUMMY_BINARY_WRONG_TYPE DUMMY_BINARY_LINUX_32 #define CURRENT_PLATFORM_DUMMY_BINARY_BAD BAD_DUMMY_BINARY_LINUX_64 #endif diff --git a/tests/framework/icd/CMakeLists.txt b/tests/framework/icd/CMakeLists.txt index 28548ef1b853ef952b601bffcf70b6e67949f260..0463f029402db7595218c8bee5e46b30ec5e12ab 100644 --- a/tests/framework/icd/CMakeLists.txt +++ b/tests/framework/icd/CMakeLists.txt @@ -41,12 +41,12 @@ AddSharedLibrary(test_icd_version_6 DEF_FILE test_icd_6 DEFINITIONS TEST_ICD_EXPORT_ICD_GPDPA=1 TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES=1 ${TEST_ICD_VERSION_2_DEFINES}) AddSharedLibrary(test_icd_version_7 DEF_FILE test_icd_7 SOURCES test_icd.cpp - DEFINITIONS TEST_ICD_EXPOSE_VERSION_7=1 ${TEST_ICD_VERSION_2_DEFINES}) -AddSharedLibrary(test_icd_version_7_with_additional_exports DEF_FILE test_icd_7_with_exports + DEFINITIONS TEST_ICD_EXPORT_ICD_GPDPA=1 TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES=1 TEST_ICD_EXPORT_VERSION_7=1 ${TEST_ICD_VERSION_2_DEFINES}) +AddSharedLibrary(test_icd_version_7_without_exports DEF_FILE test_icd_7_without_exports SOURCES test_icd.cpp - DEFINITIONS TEST_ICD_EXPOSE_VERSION_7=1 TEST_ICD_EXPORT_ICD_GPDPA=1 - TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES=1 ${TEST_ICD_VERSION_2_DEFINES}) -AddSharedLibrary(test_unicode DEF_FILE test_icd_2 + DEFINITIONS TEST_ICD_EXPORT_VERSION_7=0 TEST_ICD_EXPORT_ICD_GPDPA=1 + TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES=1 ${TEST_ICD_VERSION_2_DEFINES}) +AddSharedLibrary(test_unicode DEF_FILE 🌋 SOURCES test_icd.cpp DEFINITIONS ${TEST_ICD_VERSION_2_DEFINES}) set_target_properties(test_unicode PROPERTIES OUTPUT_NAME "🌋") # Test unicode library diff --git a/tests/framework/icd/export_definitions/test_icd_7.def b/tests/framework/icd/export_definitions/test_icd_7.def index 4414bbeabc0054bf64b638ad6a790a24fae03a8a..8cafb29614d2ae1e259b53e170a8fe2dd431af23 100644 --- a/tests/framework/icd/export_definitions/test_icd_7.def +++ b/tests/framework/icd/export_definitions/test_icd_7.def @@ -1,3 +1,6 @@ LIBRARY test_icd_version_7 EXPORTS vk_icdGetInstanceProcAddr + vk_icdNegotiateLoaderICDInterfaceVersion + vk_icdEnumerateAdapterPhysicalDevices + vk_icdGetPhysicalDeviceProcAddr diff --git a/tests/framework/icd/export_definitions/test_icd_7_without_exports.def b/tests/framework/icd/export_definitions/test_icd_7_without_exports.def new file mode 100644 index 0000000000000000000000000000000000000000..a6b541912e39437fd0f3d8de2ffee28d19d48c8b --- /dev/null +++ b/tests/framework/icd/export_definitions/test_icd_7_without_exports.def @@ -0,0 +1,3 @@ +LIBRARY test_icd_version_7_without_exports +EXPORTS + vk_icdGetInstanceProcAddr diff --git a/tests/framework/icd/export_definitions/test_icd_7_with_exports.def "b/tests/framework/icd/export_definitions/\360\237\214\213.def" similarity index 39% rename from tests/framework/icd/export_definitions/test_icd_7_with_exports.def rename to "tests/framework/icd/export_definitions/\360\237\214\213.def" index 7861805730574be2cd880a05b51ff3ab81d21d5d..1567087dfc70371a30768d23fcd5958bed4f5471 100644 --- a/tests/framework/icd/export_definitions/test_icd_7_with_exports.def +++ "b/tests/framework/icd/export_definitions/\360\237\214\213.def" @@ -1,6 +1,4 @@ -LIBRARY test_icd_version_7_with_additional_exports +LIBRARY 🌋 EXPORTS vk_icdGetInstanceProcAddr vk_icdNegotiateLoaderICDInterfaceVersion - vk_icdEnumerateAdapterPhysicalDevices - vk_icdGetPhysicalDeviceProcAddr diff --git a/tests/framework/icd/physical_device.h b/tests/framework/icd/physical_device.h deleted file mode 100644 index e88bee89b947a37b52a2c82221845fdc48b4c921..0000000000000000000000000000000000000000 --- a/tests/framework/icd/physical_device.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2021-2022 The Khronos Group Inc. - * Copyright (c) 2021-2022 Valve Corporation - * Copyright (c) 2021-2022 LunarG, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and/or associated documentation files (the "Materials"), to - * deal in the Materials without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Materials, and to permit persons to whom the Materials are - * furnished to do so, subject to the following conditions: - * - * The above copyright notice(s) and this permission notice shall be included in - * all copies or substantial portions of the Materials. - * - * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE - * USE OR OTHER DEALINGS IN THE MATERIALS. - * - * Author: Charles Giessen - */ - -#pragma once - -#include "test_util.h" - -// Move only type because it holds a DispatchableHandle -struct PhysicalDevice { - PhysicalDevice() {} - PhysicalDevice(std::string name) : deviceName(name) {} - PhysicalDevice(const char* name) : deviceName(name) {} - - DispatchableHandle vk_physical_device; - BUILDER_VALUE(PhysicalDevice, std::string, deviceName, "") - BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceProperties, properties, {}) - BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceFeatures, features, {}) - BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceMemoryProperties, memory_properties, {}) - BUILDER_VALUE(PhysicalDevice, VkImageFormatProperties, image_format_properties, {}) - BUILDER_VALUE(PhysicalDevice, VkExternalMemoryProperties, external_memory_properties, {}) - BUILDER_VALUE(PhysicalDevice, VkExternalSemaphoreProperties, external_semaphore_properties, {}) - BUILDER_VALUE(PhysicalDevice, VkExternalFenceProperties, external_fence_properties, {}) - BUILDER_VALUE(PhysicalDevice, uint32_t, pci_bus, {}) - - BUILDER_VECTOR(PhysicalDevice, MockQueueFamilyProperties, queue_family_properties, queue_family_properties) - BUILDER_VECTOR(PhysicalDevice, VkFormatProperties, format_properties, format_properties) - BUILDER_VECTOR(PhysicalDevice, VkSparseImageFormatProperties, sparse_image_format_properties, sparse_image_format_properties) - - BUILDER_VECTOR(PhysicalDevice, Extension, extensions, extension) - - BUILDER_VALUE(PhysicalDevice, VkSurfaceCapabilitiesKHR, surface_capabilities, {}) - BUILDER_VECTOR(PhysicalDevice, VkSurfaceFormatKHR, surface_formats, surface_format) - BUILDER_VECTOR(PhysicalDevice, VkPresentModeKHR, surface_present_modes, surface_present_mode) - - BUILDER_VECTOR(PhysicalDevice, VkDisplayPropertiesKHR, display_properties, display_properties) - BUILDER_VECTOR(PhysicalDevice, VkDisplayPlanePropertiesKHR, display_plane_properties, display_plane_properties) - BUILDER_VECTOR(PhysicalDevice, VkDisplayKHR, displays, displays) - BUILDER_VECTOR(PhysicalDevice, VkDisplayModePropertiesKHR, display_mode_properties, display_mode_properties) - BUILDER_VALUE(PhysicalDevice, VkDisplayModeKHR, display_mode, {}) - BUILDER_VALUE(PhysicalDevice, VkDisplayPlaneCapabilitiesKHR, display_plane_capabilities, {}) - - BUILDER_VALUE(PhysicalDevice, VkLayeredDriverUnderlyingApiMSFT, layered_driver_underlying_api, - VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT) - - PhysicalDevice& set_api_version(uint32_t version) { - properties.apiVersion = version; - return *this; - } - - PhysicalDevice&& finish() { return std::move(*this); } - - // Objects created from this physical device - std::vector device_handles; - std::vector device_create_infos; - std::vector> queue_handles; - - // Unknown physical device functions. Add a `VulkanFunction` to this list which will be searched in - // vkGetInstanceProcAddr for custom_instance_functions and vk_icdGetPhysicalDeviceProcAddr for custom_physical_device_functions. - // To add unknown device functions, add it to the PhysicalDevice directly (in the known_device_functions member) - BUILDER_VECTOR(PhysicalDevice, VulkanFunction, custom_physical_device_functions, custom_physical_device_function) - - // List of function names which are 'known' to the physical device but have test defined implementations - // The purpose of this list is so that vkGetDeviceProcAddr returns 'a real function pointer' in tests - // without actually implementing any of the logic inside of it. - BUILDER_VECTOR(PhysicalDevice, VulkanFunction, known_device_functions, device_function) -}; - -struct PhysicalDeviceGroup { - PhysicalDeviceGroup() {} - PhysicalDeviceGroup(PhysicalDevice const& physical_device) { physical_device_handles.push_back(&physical_device); } - PhysicalDeviceGroup(std::vector const& physical_devices) { - physical_device_handles.insert(physical_device_handles.end(), physical_devices.begin(), physical_devices.end()); - } - PhysicalDeviceGroup& use_physical_device(PhysicalDevice const& physical_device) { - physical_device_handles.push_back(&physical_device); - return *this; - } - - std::vector physical_device_handles; - VkBool32 subset_allocation = false; -}; diff --git a/tests/framework/icd/test_icd.cpp b/tests/framework/icd/test_icd.cpp index a8cf1b24f560cbcee7bdac8c67cd0d8f719a3f02..ee97e873b43dbc4626df099cbc8f234dfb793dcc 100644 --- a/tests/framework/icd/test_icd.cpp +++ b/tests/framework/icd/test_icd.cpp @@ -49,10 +49,11 @@ #define TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES 0 #endif -// expose vk_icdNegotiateLoaderICDInterfaceVersion, vk_icdEnumerateAdapterPhysicalDevices, and vk_icdGetPhysicalDeviceProcAddr -// through vk_icdGetInstanceProcAddr or vkGetInstanceProcAddr -#if !defined(TEST_ICD_EXPOSE_VERSION_7) -#define TEST_ICD_EXPOSE_VERSION_7 0 +// export vk_icdNegotiateLoaderICDInterfaceVersion, vk_icdEnumerateAdapterPhysicalDevices, and vk_icdGetPhysicalDeviceProcAddr +// through dlsym/GetProcAddress +// Default is *on* +#if !defined(TEST_ICD_EXPORT_VERSION_7) +#define TEST_ICD_EXPORT_VERSION_7 1 #endif TestICD icd; @@ -183,20 +184,25 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumerateInstanceVersion(uint32_t* pApiVer VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, [[maybe_unused]] const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { - if (pCreateInfo == nullptr || pCreateInfo->pApplicationInfo == nullptr) { + if (pCreateInfo == nullptr) { return VK_ERROR_OUT_OF_HOST_MEMORY; } + uint32_t default_api_version = VK_API_VERSION_1_0; + uint32_t api_version = + (pCreateInfo->pApplicationInfo == nullptr) ? default_api_version : pCreateInfo->pApplicationInfo->apiVersion; + if (icd.icd_api_version < VK_API_VERSION_1_1) { - if (pCreateInfo->pApplicationInfo->apiVersion > VK_API_VERSION_1_0) { + if (api_version > VK_API_VERSION_1_0) { return VK_ERROR_INCOMPATIBLE_DRIVER; } } // Add to the list of enabled extensions only those that the ICD actively supports - for (uint32_t iii = 0; iii < pCreateInfo->enabledExtensionCount; ++iii) { - if (IsInstanceExtensionSupported(pCreateInfo->ppEnabledExtensionNames[iii])) { - icd.add_enabled_instance_extension({pCreateInfo->ppEnabledExtensionNames[iii]}); + icd.enabled_instance_extensions.clear(); + for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { + if (IsInstanceExtensionSupported(pCreateInfo->ppEnabledExtensionNames[i])) { + icd.enabled_instance_extensions.push_back({pCreateInfo->ppEnabledExtensionNames[i]}); } } @@ -209,11 +215,16 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* } VKAPI_ATTR void VKAPI_CALL test_vkDestroyInstance([[maybe_unused]] VkInstance instance, - [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {} + [[maybe_unused]] const VkAllocationCallbacks* pAllocator) { + icd.enabled_instance_extensions.clear(); +} // VK_SUCCESS,VK_INCOMPLETE VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumeratePhysicalDevices([[maybe_unused]] VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { + if (icd.enum_physical_devices_return_code != VK_SUCCESS) { + return icd.enum_physical_devices_return_code; + } if (pPhysicalDevices == nullptr) { *pPhysicalDeviceCount = static_cast(icd.physical_devices.size()); } else { @@ -313,9 +324,16 @@ test_vkEnumeratePhysicalDeviceGroups([[maybe_unused]] VkInstance instance, uint3 VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDebugUtilsMessengerEXT( [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, - [[maybe_unused]] const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { + const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { if (nullptr != pMessenger) { - uint64_t fake_msgr_handle = reinterpret_cast(new uint8_t); + uint8_t* new_handle_ptr = nullptr; + if (pAllocator) { + new_handle_ptr = + (uint8_t*)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(uint8_t*), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + } else { + new_handle_ptr = new uint8_t; + } + uint64_t fake_msgr_handle = reinterpret_cast(new_handle_ptr); icd.messenger_handles.push_back(fake_msgr_handle); #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \ defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) @@ -329,7 +347,7 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDebugUtilsMessengerEXT( VKAPI_ATTR void VKAPI_CALL test_vkDestroyDebugUtilsMessengerEXT([[maybe_unused]] VkInstance instance, VkDebugUtilsMessengerEXT messenger, - [[maybe_unused]] const VkAllocationCallbacks* pAllocator) { + const VkAllocationCallbacks* pAllocator) { if (messenger != VK_NULL_HANDLE) { uint64_t fake_msgr_handle = (uint64_t)(messenger); auto found_iter = std::find(icd.messenger_handles.begin(), icd.messenger_handles.end(), fake_msgr_handle); @@ -337,9 +355,61 @@ VKAPI_ATTR void VKAPI_CALL test_vkDestroyDebugUtilsMessengerEXT([[maybe_unused]] // Remove it from the list icd.messenger_handles.erase(found_iter); // Delete the handle - delete (uint8_t*)fake_msgr_handle; + if (pAllocator) { + pAllocator->pfnFree(pAllocator->pUserData, (uint8_t*)fake_msgr_handle); + } else { + delete (uint8_t*)(fake_msgr_handle); + } + } else { + std::cerr << "Messenger not found during destroy!\n"; + abort(); + } + } +} + +// debug report create/destroy + +VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDebugReportCallbackEXT( + [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { + if (nullptr != pCallback) { + uint8_t* new_handle_ptr = nullptr; + if (pAllocator) { + new_handle_ptr = + (uint8_t*)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(uint8_t*), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + } else { + new_handle_ptr = new uint8_t; + } + uint64_t fake_msgr_handle = reinterpret_cast(new_handle_ptr); + icd.callback_handles.push_back(fake_msgr_handle); +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \ + defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + *pCallback = reinterpret_cast(fake_msgr_handle); +#else + *pCallback = fake_msgr_handle; +#endif + } + return VK_SUCCESS; +} + +VKAPI_ATTR void VKAPI_CALL test_vkDestroyDebugReportCallbackEXT([[maybe_unused]] VkInstance instance, + VkDebugReportCallbackEXT callback, + const VkAllocationCallbacks* pAllocator) { + if (callback != VK_NULL_HANDLE) { + uint64_t fake_msgr_handle = (uint64_t)(callback); + auto found_iter = std::find(icd.callback_handles.begin(), icd.callback_handles.end(), fake_msgr_handle); + if (found_iter != icd.callback_handles.end()) { + // Remove it from the list + icd.callback_handles.erase(found_iter); + // Delete the handle + if (pAllocator) { + pAllocator->pfnFree(pAllocator->pUserData, (uint8_t*)fake_msgr_handle); + } else { + delete (uint8_t*)(fake_msgr_handle); + } } else { - assert(false && "Messenger not found during destroy!"); + std::cerr << "callback not found during destroy!\n"; + abort(); } } } @@ -593,15 +663,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceXlibPresentationSupportKH #endif // VK_USE_PLATFORM_XLIB_KHR #if defined(VK_USE_PLATFORM_DIRECTFB_EXT) -VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDirectFBSurfaceEXT(VkInstance instance, - const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { +VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDirectFBSurfaceEXT([[maybe_unused]] VkInstance instance, + [[maybe_unused]] const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo, + [[maybe_unused]] const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface) { common_nondispatch_handle_creation(icd.surface_handles, pSurface); return VK_SUCCESS; } -VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceDirectFBPresentationSupportEXT(VkPhysicalDevice physicalDevice, - uint32_t queueFamilyIndex, IDirectFB* dfb) { +VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceDirectFBPresentationSupportEXT(VkPhysicalDevice, uint32_t, IDirectFB*) { return VK_TRUE; } @@ -628,10 +698,9 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateIOSSurfaceMVK([[maybe_unused]] VkIns #endif // VK_USE_PLATFORM_IOS_MVK #if defined(VK_USE_PLATFORM_GGP) -VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateStreamDescriptorSurfaceGGP(VkInstance instance, - const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { +VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateStreamDescriptorSurfaceGGP( + [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, + [[maybe_unused]] const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { common_nondispatch_handle_creation(icd.surface_handles, pSurface); return VK_SUCCESS; } @@ -648,15 +717,16 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateMetalSurfaceEXT([[maybe_unused]] VkI #endif // VK_USE_PLATFORM_METAL_EXT #if defined(VK_USE_PLATFORM_SCREEN_QNX) -VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateScreenSurfaceQNX(VkInstance instance, const VkScreenSurfaceCreateInfoQNX* pCreateInfo, - const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { +VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateScreenSurfaceQNX([[maybe_unused]] VkInstance instance, + [[maybe_unused]] const VkScreenSurfaceCreateInfoQNX* pCreateInfo, + [[maybe_unused]] const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface) { common_nondispatch_handle_creation(icd.surface_handles, pSurface); return VK_SUCCESS; } -VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceScreenPresentationSupportQNX(VkPhysicalDevice physicalDevice, - uint32_t queueFamilyIndex, - struct _screen_window* window) { +VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceScreenPresentationSupportQNX(VkPhysicalDevice, uint32_t, + struct _screen_window*) { return VK_TRUE; } #endif // VK_USE_PLATFORM_SCREEN_QNX @@ -768,6 +838,11 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysi assert(false && "Surface not found during GetPhysicalDeviceSurfaceFormatsKHR query!"); return VK_ERROR_UNKNOWN; } + } else { + if (!IsInstanceExtensionEnabled(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) { + assert(false && "Surface is NULL but VK_GOOGLE_surfaceless_query was not enabled!"); + return VK_ERROR_UNKNOWN; + } } FillCountPtr(icd.GetPhysDevice(physicalDevice).surface_formats, pSurfaceFormatCount, pSurfaceFormats); return VK_SUCCESS; @@ -782,10 +857,38 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfacePresentModesKHR(Vk assert(false && "Surface not found during GetPhysicalDeviceSurfacePresentModesKHR query!"); return VK_ERROR_UNKNOWN; } + } else { + if (!IsInstanceExtensionEnabled(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) { + assert(false && "Surface is NULL but VK_GOOGLE_surfaceless_query was not enabled!"); + return VK_ERROR_UNKNOWN; + } + } + FillCountPtr(icd.GetPhysDevice(physicalDevice).surface_present_modes, pPresentModeCount, pPresentModes); + return VK_SUCCESS; +} + +#if defined(WIN32) +VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfacePresentModes2EXT(VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes) { + if (pSurfaceInfo->surface != VK_NULL_HANDLE) { + uint64_t fake_surf_handle = (uint64_t)(pSurfaceInfo->surface); + auto found_iter = std::find(icd.surface_handles.begin(), icd.surface_handles.end(), fake_surf_handle); + if (found_iter == icd.surface_handles.end()) { + assert(false && "Surface not found during GetPhysicalDeviceSurfacePresentModesKHR query!"); + return VK_ERROR_UNKNOWN; + } + } else { + if (!IsInstanceExtensionEnabled(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) { + assert(false && "Surface is NULL but VK_GOOGLE_surfaceless_query was not enabled!"); + return VK_ERROR_UNKNOWN; + } } FillCountPtr(icd.GetPhysDevice(physicalDevice).surface_present_modes, pPresentModeCount, pPresentModes); return VK_SUCCESS; } +#endif // VK_KHR_display VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice, @@ -842,6 +945,62 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceCapabilities2KHR(V const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities) { if (nullptr != pSurfaceInfo && nullptr != pSurfaceCapabilities) { + if (IsInstanceExtensionSupported("VK_EXT_surface_maintenance1") && + IsInstanceExtensionEnabled("VK_EXT_surface_maintenance1")) { + auto& phys_dev = icd.GetPhysDevice(physicalDevice); + void* pNext = pSurfaceCapabilities->pNext; + while (pNext) { + VkBaseOutStructure pNext_base_structure{}; + std::memcpy(&pNext_base_structure, pNext, sizeof(VkBaseInStructure)); + if (pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT) { + // First must find the present mode that is being queried + VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAX_ENUM_KHR; + const void* pSurfaceInfo_pNext = pSurfaceInfo->pNext; + while (pSurfaceInfo_pNext) { + VkBaseInStructure pSurfaceInfo_pNext_base_structure{}; + std::memcpy(&pSurfaceInfo_pNext_base_structure, pSurfaceInfo_pNext, sizeof(VkBaseInStructure)); + if (pSurfaceInfo_pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT) { + present_mode = reinterpret_cast(pSurfaceInfo_pNext)->presentMode; + } + pSurfaceInfo_pNext = pSurfaceInfo_pNext_base_structure.pNext; + } + + VkSurfacePresentModeCompatibilityEXT* present_mode_compatibility = + reinterpret_cast(pNext); + if (present_mode == VK_PRESENT_MODE_MAX_ENUM_KHR) { + present_mode_compatibility->presentModeCount = 0; + } else { + auto it = + std::find(phys_dev.surface_present_modes.begin(), phys_dev.surface_present_modes.end(), present_mode); + if (it != phys_dev.surface_present_modes.end()) { + size_t index = it - phys_dev.surface_present_modes.begin(); + present_mode_compatibility->presentModeCount = + static_cast(phys_dev.surface_present_mode_compatibility[index].size()); + if (present_mode_compatibility->pPresentModes) { + for (size_t i = 0; i < phys_dev.surface_present_mode_compatibility[index].size(); i++) { + present_mode_compatibility->pPresentModes[i] = + phys_dev.surface_present_mode_compatibility[index][i]; + } + } + } + } + } else if (pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT) { + VkSurfacePresentScalingCapabilitiesEXT* present_scaling_capabilities = + reinterpret_cast(pNext); + present_scaling_capabilities->minScaledImageExtent = + phys_dev.surface_present_scaling_capabilities.minScaledImageExtent; + present_scaling_capabilities->maxScaledImageExtent = + phys_dev.surface_present_scaling_capabilities.maxScaledImageExtent; + present_scaling_capabilities->supportedPresentScaling = + phys_dev.surface_present_scaling_capabilities.supportedPresentScaling; + present_scaling_capabilities->supportedPresentGravityX = + phys_dev.surface_present_scaling_capabilities.supportedPresentGravityX; + present_scaling_capabilities->supportedPresentGravityY = + phys_dev.surface_present_scaling_capabilities.supportedPresentGravityY; + } + pNext = pNext_base_structure.pNext; + } + } return test_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, pSurfaceInfo->surface, &pSurfaceCapabilities->surfaceCapabilities); } @@ -865,10 +1024,16 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceFormats2KHR(VkPhys return VK_SUCCESS; } // VK_KHR_display_swapchain -VkResult test_vkCreateSharedSwapchainsKHR([[maybe_unused]] VkDevice device, uint32_t swapchainCount, - [[maybe_unused]] const VkSwapchainCreateInfoKHR* pCreateInfos, - [[maybe_unused]] const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains) { +VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateSharedSwapchainsKHR([[maybe_unused]] VkDevice device, uint32_t swapchainCount, + const VkSwapchainCreateInfoKHR* pCreateInfos, + [[maybe_unused]] const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchains) { for (uint32_t i = 0; i < swapchainCount; i++) { + uint64_t surface_integer_value = from_nondispatch_handle(pCreateInfos[i].surface); + auto found_iter = std::find(icd.surface_handles.begin(), icd.surface_handles.end(), surface_integer_value); + if (found_iter == icd.surface_handles.end()) { + return VK_ERROR_INITIALIZATION_FAILED; + } common_nondispatch_handle_creation(icd.swapchain_handles, &pSwapchains[i]); } return VK_SUCCESS; @@ -1104,6 +1269,9 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkGetCalibratedTimestampsEXT(VkDevice, uint3 VKAPI_ATTR VkResult VKAPI_CALL test_vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { + if (icd.enum_adapter_physical_devices_return_code != VK_SUCCESS) { + return icd.enum_adapter_physical_devices_return_code; + } if (adapterLUID.LowPart != icd.adapterLUID.LowPart || adapterLUID.HighPart != icd.adapterLUID.HighPart) { *pPhysicalDeviceCount = 0; return VK_ERROR_INCOMPATIBLE_DRIVER; @@ -1118,7 +1286,7 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vk_icdEnumerateAdapterPhysicalDevices(VkInst } #endif // defined(WIN32) -VkResult test_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) { +VKAPI_ATTR VkResult VKAPI_CALL test_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) { if (icd.called_vk_icd_gipa == CalledICDGIPA::not_called && icd.called_negotiate_interface == CalledNegotiateInterface::not_called) icd.called_negotiate_interface = CalledNegotiateInterface::vk_icd_negotiate; @@ -1150,21 +1318,6 @@ VkResult test_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersi return VK_SUCCESS; } -// Forward declarations for trampolines -extern "C" { -#if TEST_ICD_EXPOSE_VERSION_7 -FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion); -#if TEST_ICD_EXPORT_ICD_GPDPA -FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName); -#endif -#if defined(WIN32) && TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES -FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID, - uint32_t* pPhysicalDeviceCount, - VkPhysicalDevice* pPhysicalDevices); -#endif -#endif -} - //// trampolines PFN_vkVoidFunction get_instance_func_ver_1_1([[maybe_unused]] VkInstance instance, const char* pName) { @@ -1190,6 +1343,12 @@ PFN_vkVoidFunction get_physical_device_func_wsi([[maybe_unused]] VkInstance inst if (string_eq(pName, "vkGetPhysicalDeviceSurfacePresentModesKHR")) return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfacePresentModesKHR); } +#if defined(WIN32) + if (IsPhysicalDeviceExtensionAvailable("VK_EXT_full_screen_exclusive")) { + if (string_eq(pName, "vkGetPhysicalDeviceSurfacePresentModes2EXT")) + return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfacePresentModes2EXT); + } +#endif if (IsInstanceExtensionEnabled("VK_KHR_get_surface_capabilities2")) { if (string_eq(pName, "vkGetPhysicalDeviceSurfaceCapabilities2KHR")) return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfaceCapabilities2KHR); @@ -1313,12 +1472,20 @@ PFN_vkVoidFunction get_instance_func_wsi(VkInstance instance, const char* pName) return to_vkVoidFunction(test_vkDestroyDebugUtilsMessengerEXT); } } + if (IsInstanceExtensionEnabled(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { + if (string_eq(pName, "vkCreateDebugReportCallbackEXT")) { + return to_vkVoidFunction(test_vkCreateDebugReportCallbackEXT); + } + if (string_eq(pName, "vkDestroyDebugReportCallbackEXT")) { + return to_vkVoidFunction(test_vkDestroyDebugReportCallbackEXT); + } + } PFN_vkVoidFunction ret_phys_dev_wsi = get_physical_device_func_wsi(instance, pName); if (ret_phys_dev_wsi != nullptr) return ret_phys_dev_wsi; return nullptr; } -PFN_vkVoidFunction get_physical_device_func([[maybe_unused]] VkInstance instance, const char* pName) { +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_physical_device_func([[maybe_unused]] VkInstance instance, const char* pName) { if (string_eq(pName, "vkEnumerateDeviceLayerProperties")) return to_vkVoidFunction(test_vkEnumerateDeviceLayerProperties); if (string_eq(pName, "vkEnumerateDeviceExtensionProperties")) return to_vkVoidFunction(test_vkEnumerateDeviceExtensionProperties); @@ -1539,21 +1706,18 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL test_vkGetDeviceProcAddr(VkDevice devic PFN_vkVoidFunction base_get_instance_proc_addr(VkInstance instance, const char* pName) { if (pName == nullptr) return nullptr; if (instance == NULL) { -#if TEST_ICD_EXPOSE_VERSION_7 if (string_eq(pName, "vk_icdNegotiateLoaderICDInterfaceVersion")) return icd.exposes_vk_icdNegotiateLoaderICDInterfaceVersion - ? to_vkVoidFunction(vk_icdNegotiateLoaderICDInterfaceVersion) + ? to_vkVoidFunction(test_vk_icdNegotiateLoaderICDInterfaceVersion) : NULL; -#if TEST_ICD_EXPORT_ICD_GPDPA + if (string_eq(pName, "vk_icdGetPhysicalDeviceProcAddr")) - return icd.exposes_vk_icdGetPhysicalDeviceProcAddr ? to_vkVoidFunction(vk_icdGetPhysicalDeviceProcAddr) : NULL; -#endif -#if defined(WIN32) && TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES + return icd.exposes_vk_icdGetPhysicalDeviceProcAddr ? to_vkVoidFunction(get_physical_device_func) : NULL; +#if defined(WIN32) if (string_eq(pName, "vk_icdEnumerateAdapterPhysicalDevices")) - return icd.exposes_vk_icdEnumerateAdapterPhysicalDevices ? to_vkVoidFunction(vk_icdEnumerateAdapterPhysicalDevices) + return icd.exposes_vk_icdEnumerateAdapterPhysicalDevices ? to_vkVoidFunction(test_vk_icdEnumerateAdapterPhysicalDevices) : NULL; #endif // defined(WIN32) -#endif // TEST_ICD_EXPOSE_VERSION_7 if (string_eq(pName, "vkGetInstanceProcAddr")) return to_vkVoidFunction(test_vkGetInstanceProcAddr); if (string_eq(pName, "vkEnumerateInstanceExtensionProperties")) @@ -1579,13 +1743,13 @@ PFN_vkVoidFunction base_get_instance_proc_addr(VkInstance instance, const char* // Exported functions extern "C" { -#if TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION +#if TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION && TEST_ICD_EXPORT_VERSION_7 FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) { return test_vk_icdNegotiateLoaderICDInterfaceVersion(pSupportedVersion); } #endif // TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION -#if TEST_ICD_EXPORT_ICD_GPDPA +#if TEST_ICD_EXPORT_ICD_GPDPA && TEST_ICD_EXPORT_VERSION_7 FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName) { return get_physical_device_func(instance, pName); } @@ -1612,7 +1776,7 @@ FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProp } #endif // TEST_ICD_EXPORT_ICD_GIPA -#if TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES +#if TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES && TEST_ICD_EXPORT_VERSION_7 #if defined(WIN32) FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID, uint32_t* pPhysicalDeviceCount, diff --git a/tests/framework/icd/test_icd.h b/tests/framework/icd/test_icd.h index 8515a849390361efd34a9db4b20073de2caea960..25831894d588f55738f2a35af17f5c8c4f97afdf 100644 --- a/tests/framework/icd/test_icd.h +++ b/tests/framework/icd/test_icd.h @@ -31,8 +31,6 @@ #include "layer/layer_util.h" -#include "physical_device.h" - enum class CalledICDGIPA { not_called, vk_icd_gipa, vk_gipa }; enum class CalledNegotiateInterface { not_called, vk_icd_negotiate, vk_icd_gipa_first }; @@ -74,8 +72,87 @@ inline std::ostream& operator<<(std::ostream& os, const InterfaceVersionCheck& r } // clang-format on +// Move only type because it holds a DispatchableHandle +struct PhysicalDevice { + PhysicalDevice() {} + PhysicalDevice(std::string name) : deviceName(name) {} + PhysicalDevice(const char* name) : deviceName(name) {} + + DispatchableHandle vk_physical_device; + BUILDER_VALUE(PhysicalDevice, std::string, deviceName, "") + BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceProperties, properties, {}) + BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceFeatures, features, {}) + BUILDER_VALUE(PhysicalDevice, VkPhysicalDeviceMemoryProperties, memory_properties, {}) + BUILDER_VALUE(PhysicalDevice, VkImageFormatProperties, image_format_properties, {}) + BUILDER_VALUE(PhysicalDevice, VkExternalMemoryProperties, external_memory_properties, {}) + BUILDER_VALUE(PhysicalDevice, VkExternalSemaphoreProperties, external_semaphore_properties, {}) + BUILDER_VALUE(PhysicalDevice, VkExternalFenceProperties, external_fence_properties, {}) + BUILDER_VALUE(PhysicalDevice, uint32_t, pci_bus, {}) + + BUILDER_VECTOR(PhysicalDevice, MockQueueFamilyProperties, queue_family_properties, queue_family_properties) + BUILDER_VECTOR(PhysicalDevice, VkFormatProperties, format_properties, format_properties) + BUILDER_VECTOR(PhysicalDevice, VkSparseImageFormatProperties, sparse_image_format_properties, sparse_image_format_properties) + + BUILDER_VECTOR(PhysicalDevice, Extension, extensions, extension) + + BUILDER_VALUE(PhysicalDevice, VkSurfaceCapabilitiesKHR, surface_capabilities, {}) + BUILDER_VECTOR(PhysicalDevice, VkSurfaceFormatKHR, surface_formats, surface_format) + BUILDER_VECTOR(PhysicalDevice, VkPresentModeKHR, surface_present_modes, surface_present_mode) + BUILDER_VALUE(PhysicalDevice, VkSurfacePresentScalingCapabilitiesEXT, surface_present_scaling_capabilities, {}) + // No good way to make this a builder value. Each std::vector corresponds to each surface_present_modes + // element + std::vector> surface_present_mode_compatibility{}; + + BUILDER_VECTOR(PhysicalDevice, VkDisplayPropertiesKHR, display_properties, display_properties) + BUILDER_VECTOR(PhysicalDevice, VkDisplayPlanePropertiesKHR, display_plane_properties, display_plane_properties) + BUILDER_VECTOR(PhysicalDevice, VkDisplayKHR, displays, displays) + BUILDER_VECTOR(PhysicalDevice, VkDisplayModePropertiesKHR, display_mode_properties, display_mode_properties) + BUILDER_VALUE(PhysicalDevice, VkDisplayModeKHR, display_mode, {}) + BUILDER_VALUE(PhysicalDevice, VkDisplayPlaneCapabilitiesKHR, display_plane_capabilities, {}) + + BUILDER_VALUE(PhysicalDevice, VkLayeredDriverUnderlyingApiMSFT, layered_driver_underlying_api, + VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT) + + PhysicalDevice& set_api_version(uint32_t version) { + properties.apiVersion = version; + return *this; + } + + PhysicalDevice&& finish() { return std::move(*this); } + + // Objects created from this physical device + std::vector device_handles; + std::vector device_create_infos; + std::vector> queue_handles; + + // Unknown physical device functions. Add a `VulkanFunction` to this list which will be searched in + // vkGetInstanceProcAddr for custom_instance_functions and vk_icdGetPhysicalDeviceProcAddr for custom_physical_device_functions. + // To add unknown device functions, add it to the PhysicalDevice directly (in the known_device_functions member) + BUILDER_VECTOR(PhysicalDevice, VulkanFunction, custom_physical_device_functions, custom_physical_device_function) + + // List of function names which are 'known' to the physical device but have test defined implementations + // The purpose of this list is so that vkGetDeviceProcAddr returns 'a real function pointer' in tests + // without actually implementing any of the logic inside of it. + BUILDER_VECTOR(PhysicalDevice, VulkanFunction, known_device_functions, device_function) +}; + +struct PhysicalDeviceGroup { + PhysicalDeviceGroup() {} + PhysicalDeviceGroup(PhysicalDevice const& physical_device) { physical_device_handles.push_back(&physical_device); } + PhysicalDeviceGroup(std::vector const& physical_devices) { + physical_device_handles.insert(physical_device_handles.end(), physical_devices.begin(), physical_devices.end()); + } + PhysicalDeviceGroup& use_physical_device(PhysicalDevice const& physical_device) { + physical_device_handles.push_back(&physical_device); + return *this; + } + + std::vector physical_device_handles; + VkBool32 subset_allocation = false; +}; + struct TestICD { - fs::path manifest_file_path; + std::filesystem::path manifest_file_path; BUILDER_VALUE(TestICD, bool, exposes_vk_icdNegotiateLoaderICDInterfaceVersion, true) BUILDER_VALUE(TestICD, bool, exposes_vkEnumerateInstanceExtensionProperties, true) @@ -108,7 +185,7 @@ struct TestICD { BUILDER_VALUE(TestICD, uint32_t, icd_api_version, VK_API_VERSION_1_0) BUILDER_VECTOR(TestICD, LayerDefinition, instance_layers, instance_layer) BUILDER_VECTOR(TestICD, Extension, instance_extensions, instance_extension) - BUILDER_VECTOR(TestICD, Extension, enabled_instance_extensions, enabled_instance_extension) + std::vector enabled_instance_extensions; BUILDER_VECTOR_MOVE_ONLY(TestICD, PhysicalDevice, physical_devices, physical_device); @@ -118,6 +195,7 @@ struct TestICD { std::vector> device_handles; std::vector surface_handles; std::vector messenger_handles; + std::vector callback_handles; std::vector swapchain_handles; BUILDER_VALUE(TestICD, bool, can_query_vkEnumerateInstanceVersion, true); @@ -139,6 +217,9 @@ struct TestICD { VkInstanceCreateFlags passed_in_instance_create_flags{}; + BUILDER_VALUE(TestICD, VkResult, enum_physical_devices_return_code, VK_SUCCESS); + BUILDER_VALUE(TestICD, VkResult, enum_adapter_physical_devices_return_code, VK_SUCCESS); + PhysicalDevice& GetPhysDevice(VkPhysicalDevice physicalDevice) { for (auto& phys_dev : physical_devices) { if (phys_dev.vk_physical_device.handle == physicalDevice) return phys_dev; diff --git a/tests/framework/json_writer.h b/tests/framework/json_writer.h index d259ae6fb68d1a22cfe60501376a5c0e56843128..f22db2892e04eba123e1b9deb7a78983202a586a 100644 --- a/tests/framework/json_writer.h +++ b/tests/framework/json_writer.h @@ -27,9 +27,9 @@ #pragma once +#include #include #include -#include // Utility class to simplify the writing of JSON manifest files @@ -79,13 +79,25 @@ struct JsonWriter { void AddKeyedString(std::string const& key, std::string const& value) { CommaAndNewLine(); Indent(); - output += "\"" + key + "\": \"" + value + "\""; + output += "\"" + key + "\": \"" + escape(value) + "\""; } void AddString(std::string const& value) { CommaAndNewLine(); Indent(); - output += "\"" + value + "\""; + output += "\"" + escape(value) + "\""; } +#if defined(WIN32) + void AddKeyedString(std::string const& key, std::wstring const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": \"" + escape(narrow(value)) + "\""; + } + void AddString(std::wstring const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + escape(narrow(value)) + "\""; + } +#endif void AddKeyedBool(std::string const& key, bool value) { CommaAndNewLine(); @@ -98,6 +110,19 @@ struct JsonWriter { output += std::string(value ? "true" : "false"); } + // Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings + static std::string escape(std::string const& in_path) { + std::string out; + for (auto& c : in_path) { + if (c == '\\') + out += "\\\\"; + else + out += c; + } + return out; + } + static std::string escape(std::filesystem::path const& in_path) { return escape(narrow(in_path.native())); } + private: void CommaAndNewLine() { if (stack.size() > 0) { diff --git a/tests/framework/layer/test_layer.cpp b/tests/framework/layer/test_layer.cpp index 14f0ebd6032ad5b943f872517154e0779074a37f..93c27c73cee4fb5a793b7be5204605b114fbf46d 100644 --- a/tests/framework/layer/test_layer.cpp +++ b/tests/framework/layer/test_layer.cpp @@ -280,6 +280,37 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* if (layer.clobber_pInstance) { memset(*pInstance, 0, 128); } + PFN_vkEnumerateInstanceLayerProperties fpEnumerateInstanceLayerProperties{}; + PFN_vkEnumerateInstanceExtensionProperties fpEnumerateInstanceExtensionProperties{}; + PFN_vkEnumerateInstanceVersion fpEnumerateInstanceVersion{}; + + if (layer.query_vkEnumerateInstanceLayerProperties) { + fpEnumerateInstanceLayerProperties = reinterpret_cast( + fpGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties")); + uint32_t count = 0; + fpEnumerateInstanceLayerProperties(&count, nullptr); + if (count == 0) return VK_ERROR_LAYER_NOT_PRESENT; + std::vector layers{count, VkLayerProperties{}}; + fpEnumerateInstanceLayerProperties(&count, layers.data()); + if (count == 0) return VK_ERROR_LAYER_NOT_PRESENT; + } + if (layer.query_vkEnumerateInstanceExtensionProperties) { + fpEnumerateInstanceExtensionProperties = reinterpret_cast( + fpGetInstanceProcAddr(nullptr, "vkEnumerateInstanceExtensionProperties")); + uint32_t count = 0; + fpEnumerateInstanceExtensionProperties(nullptr, &count, nullptr); + if (count == 0) return VK_ERROR_LAYER_NOT_PRESENT; + std::vector extensions{count, VkExtensionProperties{}}; + fpEnumerateInstanceExtensionProperties(nullptr, &count, extensions.data()); + if (count == 0) return VK_ERROR_LAYER_NOT_PRESENT; + } + if (layer.query_vkEnumerateInstanceVersion) { + fpEnumerateInstanceVersion = + reinterpret_cast(fpGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); + uint32_t version = 0; + fpEnumerateInstanceVersion(&version); + if (version == 0) return VK_ERROR_LAYER_NOT_PRESENT; + } // Continue call down the chain VkResult result = fpCreateInstance(create_info_pointer, pAllocator, pInstance); @@ -297,6 +328,7 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* // next layer in the chain. layer_init_instance_dispatch_table(layer.instance_handle, &layer.instance_dispatch_table, fpGetInstanceProcAddr); + layer.enabled_instance_extensions.clear(); for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { layer.enabled_instance_extensions.push_back({pCreateInfo->ppEnabledExtensionNames[i]}); } @@ -377,6 +409,7 @@ VKAPI_ATTR void VKAPI_CALL test_vkDestroyInstance(VkInstance instance, const VkA pAllocator->pfnFree(pAllocator->pUserData, layer.spurious_instance_memory_allocation); layer.spurious_instance_memory_allocation = nullptr; } + layer.enabled_instance_extensions.clear(); layer.instance_dispatch_table.DestroyInstance(instance, pAllocator); } diff --git a/tests/framework/layer/test_layer.h b/tests/framework/layer/test_layer.h index d6d086c14d2fa688e7bd6402e5579905bbac72dc..b4ae37a0e977e1974f0b24335897655336d91b3b 100644 --- a/tests/framework/layer/test_layer.h +++ b/tests/framework/layer/test_layer.h @@ -29,6 +29,8 @@ #include "test_util.h" +#include + #include "layer/layer_util.h" #include "loader/generated/vk_layer_dispatch_table.h" @@ -89,7 +91,7 @@ struct TestLayer; using FP_layer_callback = VkResult (*)(TestLayer& layer, void* data); struct TestLayer { - fs::path manifest_file_path; + std::filesystem::path manifest_file_path; uint32_t manifest_version = VK_MAKE_API_VERSION(0, 1, 1, 2); BUILDER_VALUE(TestLayer, bool, is_meta_layer, false) @@ -195,6 +197,10 @@ struct TestLayer { // Clober the data pointed to by pDevice to overwrite the magic value BUILDER_VALUE(TestLayer, bool, clobber_pDevice, false) + BUILDER_VALUE(TestLayer, bool, query_vkEnumerateInstanceLayerProperties, false) + BUILDER_VALUE(TestLayer, bool, query_vkEnumerateInstanceExtensionProperties, false) + BUILDER_VALUE(TestLayer, bool, query_vkEnumerateInstanceVersion, false) + PFN_vkGetInstanceProcAddr next_vkGetInstanceProcAddr = VK_NULL_HANDLE; PFN_GetPhysicalDeviceProcAddr next_GetPhysicalDeviceProcAddr = VK_NULL_HANDLE; PFN_vkGetDeviceProcAddr next_vkGetDeviceProcAddr = VK_NULL_HANDLE; diff --git a/tests/framework/shim/shim.h b/tests/framework/shim/shim.h index a3a515ac7ae73c46f1c9f354e97c0927f896e8b8..68699e7d2d5ac624f646529f8bda211149a47e1a 100644 --- a/tests/framework/shim/shim.h +++ b/tests/framework/shim/shim.h @@ -59,9 +59,9 @@ enum class GpuType { unspecified, integrated, discrete, external }; struct RegistryEntry { RegistryEntry() = default; - RegistryEntry(std::string const& name) noexcept : name(name) {} - RegistryEntry(std::string const& name, DWORD value) noexcept : name(name), value(value) {} - std::string name; + RegistryEntry(std::filesystem::path const& name) noexcept : name(name) {} + RegistryEntry(std::filesystem::path const& name, DWORD value) noexcept : name(name), value(value) {} + std::filesystem::path name; DWORD value{}; }; @@ -102,9 +102,9 @@ struct DXGIAdapter { }; struct D3DKMT_Adapter { - D3DKMT_Adapter& add_driver_manifest_path(fs::path const& src); - D3DKMT_Adapter& add_implicit_layer_manifest_path(fs::path const& src); - D3DKMT_Adapter& add_explicit_layer_manifest_path(fs::path const& src); + D3DKMT_Adapter& add_driver_manifest_path(std::filesystem::path const& src); + D3DKMT_Adapter& add_implicit_layer_manifest_path(std::filesystem::path const& src); + D3DKMT_Adapter& add_explicit_layer_manifest_path(std::filesystem::path const& src); UINT hAdapter; LUID adapter_luid; @@ -113,7 +113,7 @@ struct D3DKMT_Adapter { std::vector explicit_layer_paths; private: - D3DKMT_Adapter& add_path(fs::path src, std::vector& dest); + D3DKMT_Adapter& add_path(std::filesystem::path src, std::vector& dest); }; #elif COMMON_UNIX_PLATFORMS @@ -134,27 +134,33 @@ struct FrameworkEnvironment; // forward declaration // Necessary to have inline definitions as shim is a dll and thus functions // defined in the .cpp wont be found by the rest of the application struct PlatformShim { - PlatformShim() = default; - PlatformShim(std::vector* folders) : folders(folders) {} + PlatformShim() { fputs_stderr_log.reserve(65536); } + PlatformShim(std::vector* folders) : folders(folders) { fputs_stderr_log.reserve(65536); } // Used to get info about which drivers & layers have been added to folders std::vector* folders; + // Captures the output to stderr from fputs & fputc - aka the output of loader_log() + std::string fputs_stderr_log; + // Test Framework interface void reset(); - void redirect_all_paths(fs::path const& path); - void redirect_category(fs::path const& new_path, ManifestCategory category); + void redirect_all_paths(std::filesystem::path const& path); + void redirect_category(std::filesystem::path const& new_path, ManifestCategory category); // fake paths are paths that the loader normally looks in but actually point to locations inside the test framework - void set_fake_path(ManifestCategory category, fs::path const& path); + void set_fake_path(ManifestCategory category, std::filesystem::path const& path); // known paths are real paths but since the test framework guarantee's the order files are found in, files in these paths // need to be ordered correctly - void add_known_path(fs::path const& path); + void add_known_path(std::filesystem::path const& path); + + void add_manifest(ManifestCategory category, std::filesystem::path const& path); + void add_unsecured_manifest(ManifestCategory category, std::filesystem::path const& path); - void add_manifest(ManifestCategory category, fs::path const& path); - void add_unsecured_manifest(ManifestCategory category, fs::path const& path); + void clear_logs() { fputs_stderr_log.clear(); } + bool find_in_log(std::string const& search_text) const { return fputs_stderr_log.find(search_text) != std::string::npos; } // platform specific shim interface #if defined(WIN32) @@ -164,20 +170,20 @@ struct PlatformShim { void add_dxgi_adapter(GpuType gpu_preference, DXGI_ADAPTER_DESC1 desc1); void add_d3dkmt_adapter(D3DKMT_Adapter const& adapter); - void set_app_package_path(fs::path const& path); + void set_app_package_path(std::filesystem::path const& path); std::unordered_map dxgi_adapters; std::vector d3dkmt_adapters; // TODO: - void add_CM_Device_ID(std::wstring const& id, fs::path const& icd_path, fs::path const& layer_path); + void add_CM_Device_ID(std::wstring const& id, std::filesystem::path const& icd_path, std::filesystem::path const& layer_path); std::wstring CM_device_ID_list = {L'\0'}; std::vector CM_device_ID_registry_keys; uint32_t random_base_path = 0; - std::vector icd_paths; + std::vector icd_paths; std::vector hkey_current_user_explicit_layers; std::vector hkey_current_user_implicit_layers; @@ -194,22 +200,22 @@ struct PlatformShim { std::vector created_keys; #elif COMMON_UNIX_PLATFORMS - bool is_fake_path(fs::path const& path); - fs::path const& get_real_path_from_fake_path(fs::path const& path); + bool is_fake_path(std::filesystem::path const& path); + std::filesystem::path const& get_real_path_from_fake_path(std::filesystem::path const& path); - void redirect_path(fs::path const& path, fs::path const& new_path); - void remove_redirect(fs::path const& path); + void redirect_path(std::filesystem::path const& path, std::filesystem::path const& new_path); + void remove_redirect(std::filesystem::path const& path); - bool is_known_path(fs::path const& path); - void remove_known_path(fs::path const& path); + bool is_known_path(std::filesystem::path const& path); + void remove_known_path(std::filesystem::path const& path); - void redirect_dlopen_name(fs::path const& filename, fs::path const& actual_path); - bool is_dlopen_redirect_name(fs::path const& filename); + void redirect_dlopen_name(std::filesystem::path const& filename, std::filesystem::path const& actual_path); + bool is_dlopen_redirect_name(std::filesystem::path const& filename); - fs::path query_default_redirect_path(ManifestCategory category); + std::filesystem::path query_default_redirect_path(ManifestCategory category); - std::unordered_map redirection_map; - std::unordered_map dlopen_redirection_map; + std::unordered_map redirection_map; + std::unordered_map dlopen_redirection_map; std::unordered_set known_path_set; void set_elevated_privilege(bool elev) { use_fake_elevation = elev; } @@ -227,7 +233,8 @@ struct PlatformShim { std::vector parse_env_var_list(std::string const& var); std::string category_path_name(ManifestCategory category); -std::vector get_folder_contents(std::vector* folders, std::string folder_name) noexcept; +std::vector get_folder_contents(std::vector* folders, + std::filesystem::path folder_name) noexcept; extern "C" { // dynamically link on windows and macos @@ -235,7 +242,7 @@ extern "C" { using PFN_get_platform_shim = PlatformShim* (*)(std::vector* folders); #define GET_PLATFORM_SHIM_STR "get_platform_shim" -#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) // statically link on linux PlatformShim* get_platform_shim(std::vector* folders); #endif diff --git a/tests/framework/shim/shim_common.cpp b/tests/framework/shim/shim_common.cpp index 8fe7aa0549566e855a8c4f74d535085b9aef8556..399ee8efb78fb7db4f0b3619954132c24fcfbc39 100644 --- a/tests/framework/shim/shim_common.cpp +++ b/tests/framework/shim/shim_common.cpp @@ -27,9 +27,7 @@ #include "shim.h" -#include - -void PlatformShim::redirect_all_paths(fs::path const& path) { +void PlatformShim::redirect_all_paths(std::filesystem::path const& path) { redirect_category(path, ManifestCategory::implicit_layer); redirect_category(path, ManifestCategory::explicit_layer); redirect_category(path, ManifestCategory::icd); @@ -60,7 +58,8 @@ std::vector parse_env_var_list(std::string const& var) { return items; } -std::vector get_folder_contents(std::vector* folders, std::string folder_name) noexcept { +std::vector get_folder_contents(std::vector* folders, + std::filesystem::path folder_name) noexcept { for (auto& folder : *folders) { if (folder.location() == folder_name) { return folder.get_files(); @@ -71,19 +70,16 @@ std::vector get_folder_contents(std::vector* fol #if defined(WIN32) -D3DKMT_Adapter& D3DKMT_Adapter::add_driver_manifest_path(fs::path const& src) { return add_path(src, driver_paths); } -D3DKMT_Adapter& D3DKMT_Adapter::add_implicit_layer_manifest_path(fs::path const& src) { +D3DKMT_Adapter& D3DKMT_Adapter::add_driver_manifest_path(std::filesystem::path const& src) { return add_path(src, driver_paths); } +D3DKMT_Adapter& D3DKMT_Adapter::add_implicit_layer_manifest_path(std::filesystem::path const& src) { return add_path(src, implicit_layer_paths); } -D3DKMT_Adapter& D3DKMT_Adapter::add_explicit_layer_manifest_path(fs::path const& src) { +D3DKMT_Adapter& D3DKMT_Adapter::add_explicit_layer_manifest_path(std::filesystem::path const& src) { return add_path(src, explicit_layer_paths); } -D3DKMT_Adapter& D3DKMT_Adapter::add_path(fs::path src, std::vector& dest) { - std::wstring dest_path; - dest_path.resize(src.size()); - MultiByteToWideChar(CP_UTF8, 0, src.c_str(), static_cast(src.size()), &dest_path[0], static_cast(dest_path.size())); - dest.push_back(dest_path); +D3DKMT_Adapter& D3DKMT_Adapter::add_path(std::filesystem::path src, std::vector& dest) { + dest.push_back(src.native()); return *this; } @@ -105,28 +101,28 @@ void PlatformShim::reset() { hkey_current_user_settings.clear(); } -void PlatformShim::set_fake_path([[maybe_unused]] ManifestCategory category, [[maybe_unused]] fs::path const& path) {} -void PlatformShim::add_known_path([[maybe_unused]] fs::path const& path) {} +void PlatformShim::set_fake_path([[maybe_unused]] ManifestCategory category, [[maybe_unused]] std::filesystem::path const& path) {} +void PlatformShim::add_known_path([[maybe_unused]] std::filesystem::path const& path) {} -void PlatformShim::add_manifest(ManifestCategory category, fs::path const& path) { +void PlatformShim::add_manifest(ManifestCategory category, std::filesystem::path const& path) { if (category == ManifestCategory::settings) { - hkey_local_machine_settings.emplace_back(path.str()); + hkey_local_machine_settings.emplace_back(path); } else if (category == ManifestCategory::implicit_layer) { - hkey_local_machine_implicit_layers.emplace_back(path.str()); + hkey_local_machine_implicit_layers.emplace_back(path); } else if (category == ManifestCategory::explicit_layer) { - hkey_local_machine_explicit_layers.emplace_back(path.str()); + hkey_local_machine_explicit_layers.emplace_back(path); } else { - hkey_local_machine_drivers.emplace_back(path.str()); + hkey_local_machine_drivers.emplace_back(path); } } -void PlatformShim::add_unsecured_manifest(ManifestCategory category, fs::path const& path) { +void PlatformShim::add_unsecured_manifest(ManifestCategory category, std::filesystem::path const& path) { if (category == ManifestCategory::settings) { - hkey_current_user_settings.emplace_back(path.str()); + hkey_current_user_settings.emplace_back(path); } else if (category == ManifestCategory::implicit_layer) { - hkey_current_user_implicit_layers.emplace_back(path.str()); + hkey_current_user_implicit_layers.emplace_back(path); } else if (category == ManifestCategory::explicit_layer) { - hkey_current_user_explicit_layers.emplace_back(path.str()); + hkey_current_user_explicit_layers.emplace_back(path); } } @@ -137,14 +133,11 @@ void PlatformShim::add_dxgi_adapter(GpuType gpu_preference, DXGI_ADAPTER_DESC1 d void PlatformShim::add_d3dkmt_adapter(D3DKMT_Adapter const& adapter) { d3dkmt_adapters.push_back(adapter); } -void PlatformShim::set_app_package_path(fs::path const& path) { - app_package_path.resize(path.size()); - MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, &app_package_path[0], static_cast(app_package_path.size())); -} +void PlatformShim::set_app_package_path(std::filesystem::path const& path) { app_package_path = path; } // TODO: -void PlatformShim::add_CM_Device_ID([[maybe_unused]] std::wstring const& id, [[maybe_unused]] fs::path const& icd_path, - [[maybe_unused]] fs::path const& layer_path) { +void PlatformShim::add_CM_Device_ID([[maybe_unused]] std::wstring const& id, [[maybe_unused]] std::filesystem::path const& icd_path, + [[maybe_unused]] std::filesystem::path const& layer_path) { // // append a null byte as separator if there is already id's in the list // if (CM_device_ID_list.size() != 0) { // CM_device_ID_list += L'\0'; // I'm sure this wont cause issues with std::string down the line... /s @@ -163,7 +156,7 @@ void PlatformShim::add_CM_Device_ID([[maybe_unused]] std::wstring const& id, [[m // // add_key_value_string(id_key, "VulkanLayerName", layer_path.c_str()); } -void PlatformShim::redirect_category(fs::path const&, ManifestCategory) {} +void PlatformShim::redirect_category(std::filesystem::path const&, ManifestCategory) {} #elif COMMON_UNIX_PLATFORMS @@ -181,34 +174,39 @@ std::string category_path_name(ManifestCategory category) { void PlatformShim::reset() { redirection_map.clear(); } -bool PlatformShim::is_fake_path(fs::path const& path) { return redirection_map.count(path.str()) > 0; } -fs::path const& PlatformShim::get_real_path_from_fake_path(fs::path const& path) { return redirection_map.at(path.str()); } -void PlatformShim::redirect_path(fs::path const& path, fs::path const& new_path) { redirection_map[path.str()] = new_path; } -void PlatformShim::remove_redirect(fs::path const& path) { redirection_map.erase(path.str()); } +bool PlatformShim::is_fake_path(std::filesystem::path const& path) { return redirection_map.count(path) > 0; } +std::filesystem::path const& PlatformShim::get_real_path_from_fake_path(std::filesystem::path const& path) { + return redirection_map.at(path); +} +void PlatformShim::redirect_path(std::filesystem::path const& path, std::filesystem::path const& new_path) { + redirection_map[path] = new_path; +} +void PlatformShim::remove_redirect(std::filesystem::path const& path) { redirection_map.erase(path); } -bool PlatformShim::is_known_path(fs::path const& path) { return known_path_set.count(path.str()) > 0; } -void PlatformShim::add_known_path(fs::path const& path) { known_path_set.insert(path.str()); } -void PlatformShim::remove_known_path(fs::path const& path) { known_path_set.erase(path.str()); } +bool PlatformShim::is_known_path(std::filesystem::path const& path) { return known_path_set.count(path) > 0; } +void PlatformShim::add_known_path(std::filesystem::path const& path) { known_path_set.insert(path); } +void PlatformShim::remove_known_path(std::filesystem::path const& path) { known_path_set.erase(path); } -void PlatformShim::add_manifest([[maybe_unused]] ManifestCategory category, [[maybe_unused]] fs::path const& path) {} -void PlatformShim::add_unsecured_manifest([[maybe_unused]] ManifestCategory category, [[maybe_unused]] fs::path const& path) {} +void PlatformShim::add_manifest([[maybe_unused]] ManifestCategory category, [[maybe_unused]] std::filesystem::path const& path) {} +void PlatformShim::add_unsecured_manifest([[maybe_unused]] ManifestCategory category, + [[maybe_unused]] std::filesystem::path const& path) {} void parse_and_add_env_var_override(std::vector& paths, std::string env_var_contents) { auto parsed_paths = parse_env_var_list(env_var_contents); paths.insert(paths.end(), parsed_paths.begin(), parsed_paths.end()); } -void PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory category) { +void PlatformShim::redirect_category(std::filesystem::path const& new_path, ManifestCategory category) { std::vector paths; - auto home = fs::path(get_env_var("HOME")); + auto home = std::filesystem::path(get_env_var("HOME")); if (category == ManifestCategory::settings) { redirect_path(home / ".local/share/vulkan" / category_path_name(category), new_path); return; } - if (home.size() != 0) { - paths.push_back((home / ".config").str()); - paths.push_back((home / ".local/share").str()); + if (!home.empty()) { + paths.push_back((home / ".config")); + paths.push_back((home / ".local/share")); } // Don't report errors on apple - these env-vars are not suppose to be defined bool report_errors = true; @@ -219,6 +217,9 @@ void PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory if (category == ManifestCategory::explicit_layer) { parse_and_add_env_var_override(paths, get_env_var("VK_LAYER_PATH", false)); // don't report failure } + if (category == ManifestCategory::implicit_layer) { + parse_and_add_env_var_override(paths, get_env_var("VK_IMPLICIT_LAYER_PATH", false)); // don't report failure + } parse_and_add_env_var_override(paths, FALLBACK_DATA_DIRS); parse_and_add_env_var_override(paths, FALLBACK_CONFIG_DIRS); @@ -237,23 +238,25 @@ void PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory for (auto& path : paths) { if (!path.empty()) { - redirect_path(fs::path(path) / "vulkan" / category_path_name(category), new_path); + redirect_path(std::filesystem::path(path) / "vulkan" / category_path_name(category), new_path); } } } -void PlatformShim::set_fake_path(ManifestCategory category, fs::path const& path) { +void PlatformShim::set_fake_path(ManifestCategory category, std::filesystem::path const& path) { // use /etc as the 'redirection path' by default since its always searched - redirect_path(fs::path(SYSCONFDIR) / "vulkan" / category_path_name(category), path); + redirect_path(std::filesystem::path(SYSCONFDIR) / "vulkan" / category_path_name(category), path); } -void PlatformShim::redirect_dlopen_name(fs::path const& filename, fs::path const& actual_path) { - dlopen_redirection_map[filename.str()] = actual_path; +void PlatformShim::redirect_dlopen_name(std::filesystem::path const& filename, std::filesystem::path const& actual_path) { + dlopen_redirection_map[filename] = actual_path; } -bool PlatformShim::is_dlopen_redirect_name(fs::path const& filename) { return dlopen_redirection_map.count(filename.str()) == 1; } +bool PlatformShim::is_dlopen_redirect_name(std::filesystem::path const& filename) { + return dlopen_redirection_map.count(filename) == 1; +} -fs::path PlatformShim::query_default_redirect_path(ManifestCategory category) { - return fs::path(SYSCONFDIR) / "vulkan" / category_path_name(category); +std::filesystem::path PlatformShim::query_default_redirect_path(ManifestCategory category) { + return std::filesystem::path(SYSCONFDIR) / "vulkan" / category_path_name(category); } #endif diff --git a/tests/framework/shim/unix_shim.cpp b/tests/framework/shim/unix_shim.cpp index 12b3bfa56fbb1a6bc4f2291e1359df8c672eb36c..bdf352a739bfd3249909f71aa0e110842e41246a 100644 --- a/tests/framework/shim/unix_shim.cpp +++ b/tests/framework/shim/unix_shim.cpp @@ -35,7 +35,7 @@ PlatformShim platform_shim; extern "C" { -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) PlatformShim* get_platform_shim(std::vector* folders) { platform_shim = PlatformShim(folders); return &platform_shim; @@ -48,7 +48,7 @@ FRAMEWORK_EXPORT PlatformShim* get_platform_shim(std::vector* #endif // Necessary for MacOS function shimming -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) #define OPENDIR_FUNC_NAME opendir #define READDIR_FUNC_NAME readdir #define CLOSEDIR_FUNC_NAME closedir @@ -63,6 +63,9 @@ FRAMEWORK_EXPORT PlatformShim* get_platform_shim(std::vector* #if defined(HAVE___SECURE_GETENV) #define __SECURE_GETENV_FUNC_NAME __secure_getenv #endif +#define PRINTF_FUNC_NAME printf +#define FPUTS_FUNC_NAME fputs +#define FPUTC_FUNC_NAME fputc #elif defined(__APPLE__) #define OPENDIR_FUNC_NAME my_opendir #define READDIR_FUNC_NAME my_readdir @@ -80,6 +83,8 @@ FRAMEWORK_EXPORT PlatformShim* get_platform_shim(std::vector* #define __SECURE_GETENV_FUNC_NAME my__secure_getenv #endif #endif +#define FPUTS_FUNC_NAME my_fputs +#define FPUTC_FUNC_NAME my_fputc #endif using PFN_OPENDIR = DIR* (*)(const char* path_name); @@ -93,6 +98,8 @@ using PFN_GETEGID = gid_t (*)(void); #if defined(HAVE_SECURE_GETENV) || defined(HAVE___SECURE_GETENV) using PFN_SEC_GETENV = char* (*)(const char* name); #endif +using PFN_FPUTS = int (*)(const char* str, FILE* stream); +using PFN_FPUTC = int (*)(int c, FILE* stream); #if defined(__APPLE__) #define real_opendir opendir @@ -109,6 +116,8 @@ using PFN_SEC_GETENV = char* (*)(const char* name); #if defined(HAVE___SECURE_GETENV) #define real__secure_getenv __secure_getenv #endif +#define real_fputs fputs +#define real_fputc fputc #else PFN_OPENDIR real_opendir = nullptr; PFN_READDIR real_readdir = nullptr; @@ -124,6 +133,8 @@ PFN_SEC_GETENV real_secure_getenv = nullptr; #if defined(HAVE___SECURE_GETENV) PFN_SEC_GETENV real__secure_getenv = nullptr; #endif +PFN_FPUTS real_fputs = nullptr; +PFN_FPUTC real_fputc = nullptr; #endif FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) { @@ -135,7 +146,7 @@ FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) { } DIR* dir; if (platform_shim.is_fake_path(path_name)) { - auto real_path_name = platform_shim.get_real_path_from_fake_path(fs::path(path_name)); + auto real_path_name = platform_shim.get_real_path_from_fake_path(std::filesystem::path(path_name)); dir = real_opendir(real_path_name.c_str()); platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, true}); } else if (platform_shim.is_known_path(path_name)) { @@ -150,7 +161,15 @@ FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) { FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) { #if !defined(__APPLE__) - if (!real_readdir) real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir"); + if (!real_readdir) { + if (sizeof(void*) == 8) { + real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir"); + } else { + // Necessary to specify the 64 bit readdir version since that is what is linked in when using _FILE_OFFSET_BITS + real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir64"); + } + } + #endif if (platform_shim.is_during_destruction) { return real_readdir(dir_stream); @@ -166,16 +185,25 @@ FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) { std::vector folder_contents; std::vector dirent_filenames; while (true) { + errno = 0; struct dirent* dir_entry = real_readdir(dir_stream); + if (errno != 0) { + std::cerr << "Readdir failed: errno has value of " << std::to_string(errno) << "\n"; + } if (NULL == dir_entry) { break; } + // skip . and .. entries + if ((dir_entry->d_name[0] == '.' && dir_entry->d_name[1] == '.' && dir_entry->d_name[2] == '\0') || + (dir_entry->d_name[0] == '.' && dir_entry->d_name[1] == '\0')) { + continue; + } folder_contents.push_back(dir_entry); dirent_filenames.push_back(&dir_entry->d_name[0]); } auto real_path = it->folder_path; if (it->is_fake_path) { - real_path = platform_shim.redirection_map.at(it->folder_path).str(); + real_path = platform_shim.redirection_map.at(it->folder_path); } auto filenames = get_folder_contents(platform_shim.folders, real_path); @@ -215,13 +243,13 @@ FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) { #if !defined(__APPLE__) if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access"); #endif - fs::path path{in_pathname}; + std::filesystem::path path{in_pathname}; if (!path.has_parent_path()) { return real_access(in_pathname, mode); } if (platform_shim.is_fake_path(path.parent_path())) { - fs::path real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()); + std::filesystem::path real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()); real_path /= path.filename(); return real_access(real_path.c_str(), mode); } @@ -232,7 +260,7 @@ FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode #if !defined(__APPLE__) if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen"); #endif - fs::path path{in_filename}; + std::filesystem::path path{in_filename}; if (!path.has_parent_path()) { return real_fopen(in_filename, mode); } @@ -310,6 +338,27 @@ FRAMEWORK_EXPORT char* __SECURE_GETENV_FUNC_NAME(const char* name) { } #endif #endif + +FRAMEWORK_EXPORT int FPUTS_FUNC_NAME(const char* str, FILE* stream) { +#if !defined(__APPLE__) + if (!real_fputs) real_fputs = (PFN_FPUTS)dlsym(RTLD_NEXT, "fputs"); +#endif + if (stream == stderr) { + platform_shim.fputs_stderr_log += str; + } + return real_fputs(str, stream); +} + +FRAMEWORK_EXPORT int FPUTC_FUNC_NAME(int ch, FILE* stream) { +#if !defined(__APPLE__) + if (!real_fputc) real_fputc = (PFN_FPUTC)dlsym(RTLD_NEXT, "fputc"); +#endif + if (stream == stderr) { + platform_shim.fputs_stderr_log += ch; + } + return real_fputc(ch, stream); +} + #if defined(__APPLE__) FRAMEWORK_EXPORT CFBundleRef my_CFBundleGetMainBundle() { static CFBundleRef global_bundle{}; @@ -348,8 +397,7 @@ struct Interposer { }; __attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)}; -// don't intercept readdir as it crashes when using ASAN with macOS -// __attribute__((used)) static Interposer _interpose_readdir MACOS_ATTRIB = {VOIDP_CAST(my_readdir), VOIDP_CAST(readdir)}; +__attribute__((used)) static Interposer _interpose_readdir MACOS_ATTRIB = {VOIDP_CAST(my_readdir), VOIDP_CAST(readdir)}; __attribute__((used)) static Interposer _interpose_closedir MACOS_ATTRIB = {VOIDP_CAST(my_closedir), VOIDP_CAST(closedir)}; __attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)}; __attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)}; @@ -366,6 +414,8 @@ __attribute__((used)) static Interposer _interpose__secure_getenv MACOS_ATTRIB = VOIDP_CAST(__secure_getenv)}; #endif #endif +__attribute__((used)) static Interposer _interpose_fputs MACOS_ATTRIB = {VOIDP_CAST(my_fputs), VOIDP_CAST(fputs)}; +__attribute__((used)) static Interposer _interpose_fputc MACOS_ATTRIB = {VOIDP_CAST(my_fputc), VOIDP_CAST(fputc)}; __attribute__((used)) static Interposer _interpose_CFBundleGetMainBundle MACOS_ATTRIB = {VOIDP_CAST(my_CFBundleGetMainBundle), VOIDP_CAST(CFBundleGetMainBundle)}; __attribute__((used)) static Interposer _interpose_CFBundleCopyResourcesDirectoryURL MACOS_ATTRIB = { diff --git a/tests/framework/shim/windows_shim.cpp b/tests/framework/shim/windows_shim.cpp index 8a896c1eeb4d13a8fc3a61b86c41529453278ef5..eb42fcd083e91b4d41d85a8811ee48b7e5bca162 100644 --- a/tests/framework/shim/windows_shim.cpp +++ b/tests/framework/shim/windows_shim.cpp @@ -31,6 +31,9 @@ #include #endif +#include +#include + #include "shim.h" #include "detours.h" @@ -92,9 +95,11 @@ NTSTATUS APIENTRY ShimQueryAdapterInfo(const LoaderQueryAdapterInfo *query_info) reg_info->status = LOADER_QUERY_REGISTRY_STATUS_SUCCESS; if (reg_info->output_value_size == 0) { - ULONG size = 2; // final null terminator - for (auto const &path : *paths) size = static_cast(path.length() * sizeof(wchar_t)); - // size in bytes, so multiply path size by two and add 2 for the null terminator + // final null terminator size + ULONG size = 2; + + // size is in bytes, so multiply path size + 1 (for null terminator) by size of wchar (basically, 2). + for (auto const &path : *paths) size += static_cast((path.length() + 1) * sizeof(wchar_t)); reg_info->output_value_size = size; if (size != 2) { // only want to write data if there is path data to write @@ -324,12 +329,13 @@ LSTATUS __stdcall ShimRegEnumValueA(HKEY hKey, DWORD dwIndex, LPSTR lpValueName, const auto &location = *location_ptr; if (dwIndex >= location.size()) return ERROR_NO_MORE_ITEMS; - if (*lpcchValueName < location[dwIndex].name.size()) return ERROR_NO_MORE_ITEMS; - for (size_t i = 0; i < location[dwIndex].name.size(); i++) { - lpValueName[i] = location[dwIndex].name[i]; + std::string name = narrow(location[dwIndex].name); + if (*lpcchValueName < name.size()) return ERROR_NO_MORE_ITEMS; + for (size_t i = 0; i < name.size(); i++) { + lpValueName[i] = name[i]; } - lpValueName[location[dwIndex].name.size()] = '\0'; - *lpcchValueName = static_cast(location[dwIndex].name.size() + 1); + lpValueName[name.size()] = '\0'; + *lpcchValueName = static_cast(name.size() + 1); if (*lpcbData < sizeof(DWORD)) return ERROR_NO_MORE_ITEMS; DWORD *lpcbData_dword = reinterpret_cast(lpData); *lpcbData_dword = location[dwIndex].value; @@ -343,7 +349,8 @@ LSTATUS __stdcall ShimRegCloseKey(HKEY hKey) { return ERROR_SUCCESS; } } - return ERROR_SUCCESS; + // means that RegCloseKey was called with an invalid key value (one that doesn't exist or has already been closed) + exit(-1); } // Windows app package shims @@ -401,6 +408,15 @@ LONG WINAPI ShimGetPackagePathByFullName(_In_ PCWSTR packageFullName, _Inout_ UI return 0; } +using PFN_OutputDebugStringA = void(__stdcall *)(LPCSTR lpOutputString); +static PFN_OutputDebugStringA fp_OutputDebugStringA = OutputDebugStringA; + +void __stdcall intercept_OutputDebugStringA(LPCSTR lpOutputString) { + if (lpOutputString != nullptr) { + platform_shim.fputs_stderr_log += lpOutputString; + } +} + // Initialization void WINAPI DetourFunctions() { if (!gdi32_dll) { @@ -450,6 +466,7 @@ void WINAPI DetourFunctions() { DetourAttach(&(PVOID &)fpRegCloseKey, (PVOID)ShimRegCloseKey); DetourAttach(&(PVOID &)fpGetPackagesByPackageFamily, (PVOID)ShimGetPackagesByPackageFamily); DetourAttach(&(PVOID &)fpGetPackagePathByFullName, (PVOID)ShimGetPackagePathByFullName); + DetourAttach(&(PVOID &)fp_OutputDebugStringA, (PVOID)intercept_OutputDebugStringA); LONG error = DetourTransactionCommit(); if (error != NO_ERROR) { @@ -479,6 +496,7 @@ void DetachFunctions() { DetourDetach(&(PVOID &)fpRegCloseKey, (PVOID)ShimRegCloseKey); DetourDetach(&(PVOID &)fpGetPackagesByPackageFamily, (PVOID)ShimGetPackagesByPackageFamily); DetourDetach(&(PVOID &)fpGetPackagePathByFullName, (PVOID)ShimGetPackagePathByFullName); + DetourDetach(&(PVOID &)fp_OutputDebugStringA, (PVOID)intercept_OutputDebugStringA); DetourTransactionCommit(); } diff --git a/tests/framework/test_environment.cpp b/tests/framework/test_environment.cpp index 1105f84b200dd94206b0739c3ee9e2b8bbb9a5df..fc6d011216ce07c190f8b53a5940d7b92e488a0a 100644 --- a/tests/framework/test_environment.cpp +++ b/tests/framework/test_environment.cpp @@ -27,11 +27,13 @@ #include "test_environment.h" -fs::path get_loader_path() { - auto loader_path = fs::path(FRAMEWORK_VULKAN_LIBRARY_PATH); +#include + +std::filesystem::path get_loader_path() { + auto loader_path = std::filesystem::path(FRAMEWORK_VULKAN_LIBRARY_PATH); auto env_var_res = get_env_var("VK_LOADER_TEST_LOADER_PATH", false); if (!env_var_res.empty()) { - loader_path = fs::path(env_var_res); + loader_path = std::filesystem::path(env_var_res); } return loader_path; } @@ -138,7 +140,6 @@ void init_vulkan_functions(VulkanFunctions& funcs) { funcs.vkCreateWin32SurfaceKHR = GPA(vkCreateWin32SurfaceKHR); funcs.vkGetPhysicalDeviceWin32PresentationSupportKHR = GPA(vkGetPhysicalDeviceWin32PresentationSupportKHR); #endif // VK_USE_PLATFORM_WIN32_KHR - funcs.vkDestroyDevice = GPA(vkDestroyDevice); funcs.vkGetDeviceQueue = GPA(vkGetDeviceQueue); #undef GPA @@ -153,6 +154,13 @@ VulkanFunctions::VulkanFunctions() : loader(get_loader_path()) { init_vulkan_functions(*this); } +void VulkanFunctions::load_instance_functions(VkInstance instance) { + vkCreateDebugReportCallbackEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + vkDestroyDebugReportCallbackEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + vkCreateDebugUtilsMessengerEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); + vkDestroyDebugUtilsMessengerEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")); +} + DeviceFunctions::DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device) { vkGetDeviceProcAddr = vulkan_functions.vkGetDeviceProcAddr; vkDestroyDevice = load(device, "vkDestroyDevice"); @@ -191,11 +199,15 @@ InstWrapper& InstWrapper::operator=(InstWrapper&& other) noexcept { } void InstWrapper::CheckCreate(VkResult result_to_check) { + handle_assert_null(inst); ASSERT_EQ(result_to_check, functions->vkCreateInstance(create_info.get(), callbacks, &inst)); + functions->load_instance_functions(inst); } void InstWrapper::CheckCreateWithInfo(InstanceCreateInfo& create_info, VkResult result_to_check) { + handle_assert_null(inst); ASSERT_EQ(result_to_check, functions->vkCreateInstance(create_info.get(), callbacks, &inst)); + functions->load_instance_functions(inst); } std::vector InstWrapper::GetPhysDevs(uint32_t phys_dev_count, VkResult result_to_check) { @@ -288,12 +300,14 @@ DeviceWrapper& DeviceWrapper::operator=(DeviceWrapper&& other) noexcept { } void DeviceWrapper::CheckCreate(VkPhysicalDevice phys_dev, VkResult result_to_check) { + handle_assert_null(dev); ASSERT_EQ(result_to_check, functions->vkCreateDevice(phys_dev, create_info.get(), callbacks, &dev)); } VkResult CreateDebugUtilsMessenger(DebugUtilsWrapper& debug_utils) { - return debug_utils.vkCreateDebugUtilsMessengerEXT(debug_utils.inst, debug_utils.get(), debug_utils.callbacks, - &debug_utils.messenger); + handle_assert_null(debug_utils.messenger); + return debug_utils.local_vkCreateDebugUtilsMessengerEXT(debug_utils.inst, debug_utils.get(), debug_utils.callbacks, + &debug_utils.messenger); } void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsLogger& logger) { @@ -347,7 +361,7 @@ PlatformShimWrapper::PlatformShimWrapper(std::vector* folders PlatformShimWrapper::~PlatformShimWrapper() noexcept { platform_shim->reset(); } TestICDHandle::TestICDHandle() noexcept {} -TestICDHandle::TestICDHandle(fs::path const& icd_path) noexcept : icd_library(icd_path) { +TestICDHandle::TestICDHandle(std::filesystem::path const& icd_path) noexcept : icd_library(icd_path) { proc_addr_get_test_icd = icd_library.get_symbol(GET_TEST_ICD_FUNC_STR); proc_addr_reset_icd = icd_library.get_symbol(RESET_ICD_FUNC_STR); } @@ -359,12 +373,12 @@ TestICD& TestICDHandle::reset_icd() noexcept { assert(proc_addr_reset_icd != NULL && "symbol must be loaded before use"); return *proc_addr_reset_icd(); } -fs::path TestICDHandle::get_icd_full_path() noexcept { return icd_library.lib_path; } -fs::path TestICDHandle::get_icd_manifest_path() noexcept { return manifest_path; } -fs::path TestICDHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; } +std::filesystem::path TestICDHandle::get_icd_full_path() noexcept { return icd_library.lib_path; } +std::filesystem::path TestICDHandle::get_icd_manifest_path() noexcept { return manifest_path; } +std::filesystem::path TestICDHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; } TestLayerHandle::TestLayerHandle() noexcept {} -TestLayerHandle::TestLayerHandle(fs::path const& layer_path) noexcept : layer_library(layer_path) { +TestLayerHandle::TestLayerHandle(std::filesystem::path const& layer_path) noexcept : layer_library(layer_path) { proc_addr_get_test_layer = layer_library.get_symbol(GET_TEST_LAYER_FUNC_STR); proc_addr_reset_layer = layer_library.get_symbol(RESET_LAYER_FUNC_STR); } @@ -376,9 +390,9 @@ TestLayer& TestLayerHandle::reset_layer() noexcept { assert(proc_addr_reset_layer != NULL && "symbol must be loaded before use"); return *proc_addr_reset_layer(); } -fs::path TestLayerHandle::get_layer_full_path() noexcept { return layer_library.lib_path; } -fs::path TestLayerHandle::get_layer_manifest_path() noexcept { return manifest_path; } -fs::path TestLayerHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; } +std::filesystem::path TestLayerHandle::get_layer_full_path() noexcept { return layer_library.lib_path; } +std::filesystem::path TestLayerHandle::get_layer_manifest_path() noexcept { return manifest_path; } +std::filesystem::path TestLayerHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; } FrameworkEnvironment::FrameworkEnvironment() noexcept : FrameworkEnvironment(FrameworkSettings{}) {} FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) noexcept @@ -391,6 +405,8 @@ FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) no folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("explicit_env_var_layer_folder")); folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("explicit_add_env_var_layer_folder")); folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("implicit_layer_manifests")); + folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("implicit_env_var_layer_manifests")); + folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("implicit_add_env_var_layer_manifests")); folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("override_layer_manifests")); folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("app_package_manifests")); folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("macos_bundle")); @@ -461,42 +477,44 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { folder = &get_folder(ManifestLocation::null); } if (!icd_details.is_fake) { - fs::path new_driver_name = fs::path(icd_details.icd_manifest.lib_path).stem() + "_" + std::to_string(cur_icd_index) + - fs::path(icd_details.icd_manifest.lib_path).extension(); - - auto new_driver_location = folder->copy_file(icd_details.icd_manifest.lib_path, new_driver_name.str()); + std::filesystem::path new_lib_name = icd_details.icd_manifest.lib_path.stem(); + new_lib_name += "_"; + new_lib_name += std::to_string(cur_icd_index); + new_lib_name += icd_details.icd_manifest.lib_path.extension(); + auto new_driver_location = folder->copy_file(icd_details.icd_manifest.lib_path, new_lib_name); #if COMMON_UNIX_PLATFORMS if (icd_details.library_path_type == LibraryPathType::default_search_paths) { - platform_shim->redirect_dlopen_name(new_driver_name, new_driver_location); + platform_shim->redirect_dlopen_name(new_lib_name, new_driver_location); } else if (icd_details.library_path_type == LibraryPathType::relative) { - platform_shim->redirect_dlopen_name(fs::path(SYSCONFDIR) / "vulkan" / "icd.d" / "." / new_driver_name, + platform_shim->redirect_dlopen_name(std::filesystem::path(SYSCONFDIR) / "vulkan" / "icd.d" / "." / new_lib_name, new_driver_location); } #endif #if defined(WIN32) if (icd_details.library_path_type == LibraryPathType::default_search_paths) { SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_USER_DIRS); - AddDllDirectory(conver_str_to_wstr(new_driver_location.parent_path().str()).c_str()); + AddDllDirectory(new_driver_location.parent_path().native().c_str()); } #endif icds.push_back(TestICDHandle(new_driver_location)); icds.back().reset_icd(); if (icd_details.library_path_type == LibraryPathType::relative) { - icd_details.icd_manifest.lib_path = fs::path(".") / new_driver_name; + icd_details.icd_manifest.lib_path = std::filesystem::path(".") / new_lib_name; } else if (icd_details.library_path_type == LibraryPathType::default_search_paths) { - icd_details.icd_manifest.lib_path = new_driver_name.str(); + icd_details.icd_manifest.lib_path = new_lib_name; } else { - icd_details.icd_manifest.lib_path = new_driver_location.str(); + icd_details.icd_manifest.lib_path = new_driver_location; } } if (icd_details.discovery_type != ManifestDiscoveryType::none) { - std::string full_json_name = icd_details.json_name; + std::filesystem::path new_manifest_path = icd_details.json_name.stem(); if (!icd_details.disable_icd_inc) { - full_json_name += "_" + std::to_string(cur_icd_index); + new_manifest_path += "_"; + new_manifest_path += std::to_string(cur_icd_index); } - full_json_name += ".json"; - icds.back().manifest_path = folder->write_manifest(full_json_name, icd_details.icd_manifest.get_manifest_str()); + new_manifest_path += ".json"; + icds.back().manifest_path = folder->write_manifest(new_manifest_path, icd_details.icd_manifest.get_manifest_str()); icds.back().shimmed_manifest_path = icds.back().manifest_path; switch (icd_details.discovery_type) { default: @@ -504,22 +522,22 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path); #if COMMON_UNIX_PLATFORMS icds.back().shimmed_manifest_path = - platform_shim->query_default_redirect_path(ManifestCategory::icd) / full_json_name; + platform_shim->query_default_redirect_path(ManifestCategory::icd) / new_manifest_path; #endif break; case (ManifestDiscoveryType::env_var): if (icd_details.is_dir) { - env_var_vk_icd_filenames.add_to_list(folder->location().str()); + env_var_vk_icd_filenames.add_to_list(folder->location()); } else { - env_var_vk_icd_filenames.add_to_list((folder->location() / full_json_name).str()); + env_var_vk_icd_filenames.add_to_list(folder->location() / new_manifest_path); } platform_shim->add_known_path(folder->location()); break; case (ManifestDiscoveryType::add_env_var): if (icd_details.is_dir) { - add_env_var_vk_icd_filenames.add_to_list(folder->location().str()); + add_env_var_vk_icd_filenames.add_to_list(folder->location()); } else { - add_env_var_vk_icd_filenames.add_to_list((folder->location() / full_json_name).str()); + add_env_var_vk_icd_filenames.add_to_list(folder->location() / new_manifest_path); } platform_shim->add_known_path(folder->location()); break; @@ -568,20 +586,40 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife if (category == ManifestCategory::implicit_layer) fs_ptr = &get_folder(ManifestLocation::implicit_layer); break; case (ManifestDiscoveryType::env_var): - fs_ptr = &get_folder(ManifestLocation::explicit_layer_env_var); - if (layer_details.is_dir) { - env_var_vk_layer_paths.add_to_list(fs_ptr->location().str()); - } else { - env_var_vk_layer_paths.add_to_list((fs_ptr->location() / layer_details.json_name).str()); + if (category == ManifestCategory::explicit_layer) { + fs_ptr = &get_folder(ManifestLocation::explicit_layer_env_var); + if (layer_details.is_dir) { + env_var_vk_layer_paths.add_to_list(fs_ptr->location()); + } else { + env_var_vk_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); + } + } + if (category == ManifestCategory::implicit_layer) { + fs_ptr = &get_folder(ManifestLocation::implicit_layer_env_var); + if (layer_details.is_dir) { + env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location()); + } else { + env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); + } } platform_shim->add_known_path(fs_ptr->location()); break; case (ManifestDiscoveryType::add_env_var): - fs_ptr = &get_folder(ManifestLocation::explicit_layer_add_env_var); - if (layer_details.is_dir) { - add_env_var_vk_layer_paths.add_to_list(fs_ptr->location().str()); - } else { - add_env_var_vk_layer_paths.add_to_list((fs_ptr->location() / layer_details.json_name).str()); + if (category == ManifestCategory::explicit_layer) { + fs_ptr = &get_folder(ManifestLocation::explicit_layer_add_env_var); + if (layer_details.is_dir) { + add_env_var_vk_layer_paths.add_to_list(fs_ptr->location()); + } else { + add_env_var_vk_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); + } + } + if (category == ManifestCategory::implicit_layer) { + fs_ptr = &get_folder(ManifestLocation::implicit_layer_add_env_var); + if (layer_details.is_dir) { + add_env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location()); + } else { + add_env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); + } } platform_shim->add_known_path(fs_ptr->location()); break; @@ -594,6 +632,9 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife case (ManifestDiscoveryType::unsecured_generic): fs_ptr = &(get_folder(ManifestLocation::unsecured_location)); break; + case (ManifestDiscoveryType::windows_app_package): + fs_ptr = &(get_folder(ManifestLocation::windows_app_package)); + break; case (ManifestDiscoveryType::none): case (ManifestDiscoveryType::null_dir): fs_ptr = &(get_folder(ManifestLocation::null)); @@ -602,39 +643,43 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife auto& folder = *fs_ptr; size_t new_layers_start = layers.size(); for (auto& layer : layer_details.layer_manifest.layers) { - if (!layer.lib_path.str().empty()) { - fs::path layer_binary_name = - layer.lib_path.filename().stem() + "_" + std::to_string(layers.size()) + layer.lib_path.filename().extension(); + if (!layer.lib_path.empty()) { + std::filesystem::path new_lib_path = layer.lib_path.stem(); + new_lib_path += "_"; + new_lib_path += std::to_string(layers.size()); + new_lib_path += layer.lib_path.extension(); - auto new_layer_location = folder.copy_file(layer.lib_path, layer_binary_name.str()); + auto new_layer_location = folder.copy_file(layer.lib_path, new_lib_path); #if COMMON_UNIX_PLATFORMS if (layer_details.library_path_type == LibraryPathType::default_search_paths) { - platform_shim->redirect_dlopen_name(layer_binary_name, new_layer_location); + platform_shim->redirect_dlopen_name(new_lib_path, new_layer_location); } if (layer_details.library_path_type == LibraryPathType::relative) { platform_shim->redirect_dlopen_name( - fs::path(SYSCONFDIR) / "vulkan" / category_path_name(category) / "." / layer_binary_name, new_layer_location); + std::filesystem::path(SYSCONFDIR) / "vulkan" / category_path_name(category) / "." / new_lib_path, + new_layer_location); } #endif #if defined(WIN32) if (layer_details.library_path_type == LibraryPathType::default_search_paths) { SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_USER_DIRS); - AddDllDirectory(conver_str_to_wstr(new_layer_location.parent_path().str()).c_str()); + AddDllDirectory(new_layer_location.parent_path().native().c_str()); } #endif // Don't load the layer binary if using any of the wrap objects layers, since it doesn't export the same interface // functions if (!layer_details.is_fake && - layer.lib_path.stem().str().find(fs::path(TEST_LAYER_WRAP_OBJECTS).stem().str()) == std::string::npos) { + layer.lib_path.stem().string().find(std::filesystem::path(TEST_LAYER_WRAP_OBJECTS).stem().string()) == + std::string::npos) { layers.push_back(TestLayerHandle(new_layer_location)); layers.back().reset_layer(); } if (layer_details.library_path_type == LibraryPathType::relative) { - layer.lib_path = fs::path(".") / layer_binary_name; + layer.lib_path = std::filesystem::path(".") / new_lib_path; } else if (layer_details.library_path_type == LibraryPathType::default_search_paths) { - layer.lib_path = layer_binary_name; + layer.lib_path = new_lib_path; } else { layer.lib_path = new_layer_location; } @@ -650,6 +695,11 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife if (layer_details.discovery_type == ManifestDiscoveryType::unsecured_generic) { platform_shim->add_unsecured_manifest(category, layer_manifest_loc); } +#if defined(_WIN32) + if (layer_details.discovery_type == ManifestDiscoveryType::windows_app_package) { + platform_shim->set_app_package_path(folder.location()); + } +#endif for (size_t i = new_layers_start; i < layers.size(); i++) { layers.at(i).manifest_path = layer_manifest_loc; layers.at(i).shimmed_manifest_path = layer_manifest_loc; @@ -689,7 +739,7 @@ std::string get_loader_settings_file_contents(const LoaderSettings& loader_setti for (const auto& config : setting.layer_configurations) { writer.StartObject(); writer.AddKeyedString("name", config.name); - writer.AddKeyedString("path", fs::fixup_backslashes_in_path(config.path)); + writer.AddKeyedString("path", config.path.native()); writer.AddKeyedString("control", config.control); writer.AddKeyedBool("treat_as_implicit_manifest", config.treat_as_implicit_manifest); writer.EndObject(); @@ -747,20 +797,40 @@ void FrameworkEnvironment::update_loader_settings(const LoaderSettings& settings void FrameworkEnvironment::remove_loader_settings() { get_folder(ManifestLocation::settings_location).remove("vk_loader_settings.json"); } +void FrameworkEnvironment::write_file_from_source(const char* source_file, ManifestCategory category, ManifestLocation location, + std::string const& file_name) { + std::fstream file{source_file, std::ios_base::in}; + ASSERT_TRUE(file.is_open()); + std::stringstream file_stream; + file_stream << file.rdbuf(); + + auto out_path = get_folder(location).write_manifest(file_name, file_stream.str()); + + if (settings.secure_loader_settings) + platform_shim->add_manifest(category, out_path); + else + platform_shim->add_unsecured_manifest(category, out_path); +} TestICD& FrameworkEnvironment::get_test_icd(size_t index) noexcept { return icds[index].get_test_icd(); } TestICD& FrameworkEnvironment::reset_icd(size_t index) noexcept { return icds[index].reset_icd(); } -fs::path FrameworkEnvironment::get_test_icd_path(size_t index) noexcept { return icds[index].get_icd_full_path(); } -fs::path FrameworkEnvironment::get_icd_manifest_path(size_t index) noexcept { return icds[index].get_icd_manifest_path(); } -fs::path FrameworkEnvironment::get_shimmed_icd_manifest_path(size_t index) noexcept { +std::filesystem::path FrameworkEnvironment::get_test_icd_path(size_t index) noexcept { return icds[index].get_icd_full_path(); } +std::filesystem::path FrameworkEnvironment::get_icd_manifest_path(size_t index) noexcept { + return icds[index].get_icd_manifest_path(); +} +std::filesystem::path FrameworkEnvironment::get_shimmed_icd_manifest_path(size_t index) noexcept { return icds[index].get_shimmed_manifest_path(); } TestLayer& FrameworkEnvironment::get_test_layer(size_t index) noexcept { return layers[index].get_test_layer(); } TestLayer& FrameworkEnvironment::reset_layer(size_t index) noexcept { return layers[index].reset_layer(); } -fs::path FrameworkEnvironment::get_test_layer_path(size_t index) noexcept { return layers[index].get_layer_full_path(); } -fs::path FrameworkEnvironment::get_layer_manifest_path(size_t index) noexcept { return layers[index].get_layer_manifest_path(); } -fs::path FrameworkEnvironment::get_shimmed_layer_manifest_path(size_t index) noexcept { +std::filesystem::path FrameworkEnvironment::get_test_layer_path(size_t index) noexcept { + return layers[index].get_layer_full_path(); +} +std::filesystem::path FrameworkEnvironment::get_layer_manifest_path(size_t index) noexcept { + return layers[index].get_layer_manifest_path(); +} +std::filesystem::path FrameworkEnvironment::get_shimmed_layer_manifest_path(size_t index) noexcept { return layers[index].get_shimmed_manifest_path(); } @@ -773,7 +843,7 @@ fs::FolderManager const& FrameworkEnvironment::get_folder(ManifestLocation locat } #if defined(__APPLE__) void FrameworkEnvironment::setup_macos_bundle() noexcept { - platform_shim->bundle_contents = get_folder(ManifestLocation::macos_bundle).location().str(); + platform_shim->bundle_contents = get_folder(ManifestLocation::macos_bundle).location(); } #endif @@ -870,6 +940,11 @@ VkResult create_surface(InstWrapper& inst, VkSurfaceKHR& surface, const char* ap return create_surface(inst.functions, inst.inst, surface, api_selection); } +VkResult create_debug_callback(InstWrapper& inst, const VkDebugReportCallbackCreateInfoEXT& create_info, + VkDebugReportCallbackEXT& callback) { + return inst.functions->vkCreateDebugReportCallbackEXT(inst.inst, &create_info, nullptr, &callback); +} + extern "C" { void __ubsan_on_report() { FAIL() << "Encountered an undefined behavior sanitizer error"; } void __asan_on_error() { FAIL() << "Encountered an address sanitizer error"; } diff --git a/tests/framework/test_environment.h b/tests/framework/test_environment.h index 3f6bf519db95c90b425b09481700011abac8a454..903bfd8963fc66c9baace8efcc74ce470093fc5a 100644 --- a/tests/framework/test_environment.h +++ b/tests/framework/test_environment.h @@ -67,11 +67,12 @@ #include "shim/shim.h" -#include "icd/physical_device.h" #include "icd/test_icd.h" #include "layer/test_layer.h" +#include FRAMEWORK_CONFIG_HEADER + // Useful defines #if COMMON_UNIX_PLATFORMS #define HOME_DIR "/home/fake_home" @@ -167,8 +168,6 @@ struct VulkanFunctions { PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr; PFN_vkCreateDevice vkCreateDevice = nullptr; - PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr; - PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr; // WSI PFN_vkCreateHeadlessSurfaceEXT vkCreateHeadlessSurfaceEXT = nullptr; @@ -231,12 +230,20 @@ struct VulkanFunctions { #endif // VK_USE_PLATFORM_WIN32_KHR PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = nullptr; + // instance extensions functions (can only be loaded with a valid instance) + PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr; // Null unless the extension is enabled + PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr; // Null unless the extension is enabled + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = nullptr; // Null unless the extension is enabled + PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT = nullptr; // Null unless the extension is enabled + // device functions PFN_vkDestroyDevice vkDestroyDevice = nullptr; PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr; VulkanFunctions(); + void load_instance_functions(VkInstance instance); + FromVoidStarFunc load(VkInstance inst, const char* func_name) const { return FromVoidStarFunc(vkGetInstanceProcAddr(inst, func_name)); } @@ -338,6 +345,41 @@ struct DeviceWrapper { DeviceCreateInfo create_info{}; }; +template +struct WrappedHandle { + WrappedHandle(HandleType in_handle, ParentType in_parent, DestroyFuncType in_destroy_func, + VkAllocationCallbacks* in_callbacks = nullptr) + : handle(in_handle), parent(in_parent), destroy_func(in_destroy_func), callbacks(in_callbacks) {} + ~WrappedHandle() { + if (handle) { + destroy_func(parent, handle, callbacks); + handle = VK_NULL_HANDLE; + } + } + WrappedHandle(WrappedHandle const&) = delete; + WrappedHandle& operator=(WrappedHandle const&) = delete; + WrappedHandle(WrappedHandle&& other) noexcept + : handle(other.handle), parent(other.parent), destroy_func(other.destroy_func), callbacks(other.callbacks) { + other.handle = VK_NULL_HANDLE; + } + WrappedHandle& operator=(WrappedHandle&& other) noexcept { + if (handle != VK_NULL_HANDLE) { + destroy_func(parent, handle, callbacks); + } + handle = other.handle; + other.handle = VK_NULL_HANDLE; + parent = other.parent; + destroy_func = other.destroy_func; + callbacks = other.callbacks; + return *this; + } + + HandleType handle = VK_NULL_HANDLE; + ParentType parent = VK_NULL_HANDLE; + DestroyFuncType destroy_func = nullptr; + VkAllocationCallbacks* callbacks = nullptr; +}; + struct DebugUtilsLogger { static VkBool32 VKAPI_PTR DebugUtilsMessengerLoggerCallback([[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -369,6 +411,16 @@ struct DebugUtilsLogger { DebugUtilsLogger& operator=(DebugUtilsLogger&&) = delete; // Find a string in the log output bool find(std::string const& search_text) const { return returned_output.find(search_text) != std::string::npos; } + // Find the number of times a string appears in the log output + uint32_t count(std::string const& search_text) const { + uint32_t occurrences = 0; + std::string::size_type position = 0; + while ((position = returned_output.find(search_text, position)) != std::string::npos) { + ++occurrences; + position += search_text.length(); + } + return occurrences; + } // Look through the event log. If you find a line containing the prefix we're interested in, look for the end of // line character, and then see if the postfix occurs in it as well. @@ -387,15 +439,17 @@ struct DebugUtilsWrapper { VkDebugUtilsMessageSeverityFlagsEXT severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VkAllocationCallbacks* callbacks = nullptr) - : logger(severity), inst(inst_wrapper.inst), callbacks(callbacks) { - vkCreateDebugUtilsMessengerEXT = reinterpret_cast( - inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkCreateDebugUtilsMessengerEXT")); - vkDestroyDebugUtilsMessengerEXT = reinterpret_cast( - inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkDestroyDebugUtilsMessengerEXT")); - }; + : logger(severity), + inst(inst_wrapper.inst), + callbacks(callbacks), + local_vkCreateDebugUtilsMessengerEXT( + FromVoidStarFunc(inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkCreateDebugUtilsMessengerEXT"))), + local_vkDestroyDebugUtilsMessengerEXT(FromVoidStarFunc( + inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkDestroyDebugUtilsMessengerEXT"))){}; ~DebugUtilsWrapper() noexcept { if (messenger) { - vkDestroyDebugUtilsMessengerEXT(inst, messenger, callbacks); + local_vkDestroyDebugUtilsMessengerEXT(inst, messenger, callbacks); + messenger = VK_NULL_HANDLE; } } // Immoveable object @@ -405,13 +459,14 @@ struct DebugUtilsWrapper { DebugUtilsWrapper& operator=(DebugUtilsWrapper&&) = delete; bool find(std::string const& search_text) { return logger.find(search_text); } + uint32_t count(std::string const& search_text) { return logger.count(search_text); } VkDebugUtilsMessengerCreateInfoEXT* get() noexcept { return logger.get(); } DebugUtilsLogger logger; VkInstance inst = VK_NULL_HANDLE; VkAllocationCallbacks* callbacks = nullptr; - PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr; - PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr; + PFN_vkCreateDebugUtilsMessengerEXT local_vkCreateDebugUtilsMessengerEXT = nullptr; + PFN_vkDestroyDebugUtilsMessengerEXT local_vkDestroyDebugUtilsMessengerEXT = nullptr; VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE; }; @@ -424,7 +479,7 @@ void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsWrap struct LoaderSettingsLayerConfiguration { BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::string, name, {}) - BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::string, path, {}) + BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::filesystem::path, path, {}) BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::string, control, {}) BUILDER_VALUE(LoaderSettingsLayerConfiguration, bool, treat_as_implicit_manifest, false) }; @@ -475,35 +530,39 @@ struct PlatformShimWrapper { struct TestICDHandle { TestICDHandle() noexcept; - TestICDHandle(fs::path const& icd_path) noexcept; + TestICDHandle(std::filesystem::path const& icd_path) noexcept; TestICD& reset_icd() noexcept; TestICD& get_test_icd() noexcept; - fs::path get_icd_full_path() noexcept; - fs::path get_icd_manifest_path() noexcept; - fs::path get_shimmed_manifest_path() noexcept; + std::filesystem::path get_icd_full_path() noexcept; + std::filesystem::path get_icd_manifest_path() noexcept; + std::filesystem::path get_shimmed_manifest_path() noexcept; // Must use statically LibraryWrapper icd_library; GetTestICDFunc proc_addr_get_test_icd = nullptr; GetNewTestICDFunc proc_addr_reset_icd = nullptr; - fs::path manifest_path; // path to the manifest file is on the actual filesystem (aka /tests/framework/<...>) - fs::path shimmed_manifest_path; // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>) + std::filesystem::path + manifest_path; // path to the manifest file is on the actual filesystem (aka /tests/framework/<...>) + std::filesystem::path + shimmed_manifest_path; // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>) }; struct TestLayerHandle { TestLayerHandle() noexcept; - TestLayerHandle(fs::path const& layer_path) noexcept; + TestLayerHandle(std::filesystem::path const& layer_path) noexcept; TestLayer& reset_layer() noexcept; TestLayer& get_test_layer() noexcept; - fs::path get_layer_full_path() noexcept; - fs::path get_layer_manifest_path() noexcept; - fs::path get_shimmed_manifest_path() noexcept; + std::filesystem::path get_layer_full_path() noexcept; + std::filesystem::path get_layer_manifest_path() noexcept; + std::filesystem::path get_shimmed_manifest_path() noexcept; // Must use statically LibraryWrapper layer_library; GetTestLayerFunc proc_addr_get_test_layer = nullptr; GetNewTestLayerFunc proc_addr_reset_layer = nullptr; - fs::path manifest_path; // path to the manifest file is on the actual filesystem (aka /tests/framework/<...>) - fs::path shimmed_manifest_path; // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>) + std::filesystem::path + manifest_path; // path to the manifest file is on the actual filesystem (aka /tests/framework/<...>) + std::filesystem::path + shimmed_manifest_path; // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>) }; // Controls whether to create a manifest and where to put it @@ -527,11 +586,11 @@ enum class LibraryPathType { struct TestICDDetails { TestICDDetails(ManifestICD icd_manifest) noexcept : icd_manifest(icd_manifest) {} - TestICDDetails(fs::path icd_binary_path, uint32_t api_version = VK_API_VERSION_1_0) noexcept { - icd_manifest.set_lib_path(icd_binary_path.str()).set_api_version(api_version); + TestICDDetails(std::filesystem::path icd_binary_path, uint32_t api_version = VK_API_VERSION_1_0) noexcept { + icd_manifest.set_lib_path(icd_binary_path).set_api_version(api_version); } BUILDER_VALUE(TestICDDetails, ManifestICD, icd_manifest, {}); - BUILDER_VALUE(TestICDDetails, std::string, json_name, "test_icd"); + BUILDER_VALUE(TestICDDetails, std::filesystem::path, json_name, "test_icd"); // Uses the json_name without modification - default is to append _1 in the json file to distinguish drivers BUILDER_VALUE(TestICDDetails, bool, disable_icd_inc, false); BUILDER_VALUE(TestICDDetails, ManifestDiscoveryType, discovery_type, ManifestDiscoveryType::generic); @@ -563,11 +622,13 @@ enum class ManifestLocation { explicit_layer_env_var = 4, explicit_layer_add_env_var = 5, implicit_layer = 6, - override_layer = 7, - windows_app_package = 8, - macos_bundle = 9, - unsecured_location = 10, - settings_location = 11, + implicit_layer_env_var = 7, + implicit_layer_add_env_var = 8, + override_layer = 9, + windows_app_package = 10, + macos_bundle = 11, + unsecured_location = 12, + settings_location = 13, }; struct FrameworkSettings { @@ -599,17 +660,20 @@ struct FrameworkEnvironment { void update_loader_settings(const LoaderSettings& loader_settings) noexcept; void remove_loader_settings(); + void write_file_from_source(const char* source_file, ManifestCategory category, ManifestLocation location, + std::string const& file_name); + TestICD& get_test_icd(size_t index = 0) noexcept; TestICD& reset_icd(size_t index = 0) noexcept; - fs::path get_test_icd_path(size_t index = 0) noexcept; - fs::path get_icd_manifest_path(size_t index = 0) noexcept; - fs::path get_shimmed_icd_manifest_path(size_t index = 0) noexcept; + std::filesystem::path get_test_icd_path(size_t index = 0) noexcept; + std::filesystem::path get_icd_manifest_path(size_t index = 0) noexcept; + std::filesystem::path get_shimmed_icd_manifest_path(size_t index = 0) noexcept; TestLayer& get_test_layer(size_t index = 0) noexcept; TestLayer& reset_layer(size_t index = 0) noexcept; - fs::path get_test_layer_path(size_t index = 0) noexcept; - fs::path get_layer_manifest_path(size_t index = 0) noexcept; - fs::path get_shimmed_layer_manifest_path(size_t index = 0) noexcept; + std::filesystem::path get_test_layer_path(size_t index = 0) noexcept; + std::filesystem::path get_layer_manifest_path(size_t index = 0) noexcept; + std::filesystem::path get_shimmed_layer_manifest_path(size_t index = 0) noexcept; fs::FolderManager& get_folder(ManifestLocation location) noexcept; fs::FolderManager const& get_folder(ManifestLocation location) const noexcept; @@ -639,6 +703,8 @@ struct FrameworkEnvironment { EnvVarWrapper add_env_var_vk_icd_filenames{"VK_ADD_DRIVER_FILES"}; EnvVarWrapper env_var_vk_layer_paths{"VK_LAYER_PATH"}; EnvVarWrapper add_env_var_vk_layer_paths{"VK_ADD_LAYER_PATH"}; + EnvVarWrapper env_var_vk_implicit_layer_paths{"VK_IMPLICIT_LAYER_PATH"}; + EnvVarWrapper add_env_var_vk_implicit_layer_paths{"VK_ADD_IMPLICIT_LAYER_PATH"}; LoaderSettings loader_settings; // the current settings written to disk private: @@ -653,3 +719,6 @@ struct FrameworkEnvironment { VkResult create_surface(InstWrapper& inst, VkSurfaceKHR& out_surface, const char* api_selection = nullptr); // Alternate parameter list for allocation callback tests VkResult create_surface(VulkanFunctions* functions, VkInstance inst, VkSurfaceKHR& surface, const char* api_selection = nullptr); + +VkResult create_debug_callback(InstWrapper& inst, const VkDebugReportCallbackCreateInfoEXT& create_info, + VkDebugReportCallbackEXT& callback); diff --git a/tests/framework/test_util.cpp b/tests/framework/test_util.cpp index 6952a480d2f97bb950694f9f69d0728947acaf8b..2a433c703c956a9264b38e2b52659a141711fc88 100644 --- a/tests/framework/test_util.cpp +++ b/tests/framework/test_util.cpp @@ -27,7 +27,10 @@ #include "test_util.h" +#include + #if defined(WIN32) +#include #include const char* win_api_error_str(LSTATUS status) { if (status == ERROR_INVALID_FUNCTION) return "ERROR_INVALID_FUNCTION"; @@ -113,8 +116,16 @@ void print_array_of_t(JsonWriter& writer, const char* object_name, std::vector const& strings) { if (strings.size() == 0) return; writer.StartKeyedArray(object_name); - for (auto& str : strings) { - writer.AddString(fs::fixup_backslashes_in_path(str)); + for (auto const& str : strings) { + writer.AddString(std::filesystem::path(str).native()); + } + writer.EndArray(); +} +void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector const& paths) { + if (paths.size() == 0) return; + writer.StartKeyedArray(object_name); + for (auto const& path : paths) { + writer.AddString(path.native()); } writer.EndArray(); } @@ -126,7 +137,7 @@ std::string ManifestICD::get_manifest_str() const { writer.StartObject(); writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); writer.StartKeyedObject("ICD"); - writer.AddKeyedString("library_path", fs::fixup_backslashes_in_path(lib_path).str()); + writer.AddKeyedString("library_path", lib_path.native()); writer.AddKeyedString("api_version", version_to_string(api_version)); writer.AddKeyedBool("is_portability_driver", is_portability_driver); if (!library_arch.empty()) writer.AddKeyedString("library_arch", library_arch); @@ -147,8 +158,8 @@ void ManifestLayer::LayerDescription::Extension::get_manifest_str(JsonWriter& wr void ManifestLayer::LayerDescription::get_manifest_str(JsonWriter& writer) const { writer.AddKeyedString("name", name); writer.AddKeyedString("type", get_type_str(type)); - if (!lib_path.str().empty()) { - writer.AddKeyedString("library_path", fs::fixup_backslashes_in_path(lib_path.str())); + if (!lib_path.empty()) { + writer.AddKeyedString("library_path", lib_path.native()); } writer.AddKeyedString("api_version", version_to_string(api_version)); writer.AddKeyedString("implementation_version", std::to_string(implementation_version)); @@ -207,169 +218,34 @@ std::string ManifestLayer::get_manifest_str() const { } namespace fs { -std::string make_native(std::string const& in_path) { - std::string out; -#if defined(WIN32) - for (auto& c : in_path) { - if (c == '/') - out += "\\"; - else - out += c; - } -#elif COMMON_UNIX_PLATFORMS - for (size_t i = 0; i < in_path.size(); i++) { - if (i + 1 < in_path.size() && in_path[i] == '\\' && in_path[i + 1] == '\\') { - out += '/'; - i++; - } else - out += in_path[i]; - } -#endif - return out; -} - -// Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings -std::string fixup_backslashes_in_path(std::string const& in_path) { - std::string out; - for (auto& c : in_path) { - if (c == '\\') - out += "\\\\"; - else - out += c; - } - return out; -} -fs::path fixup_backslashes_in_path(fs::path const& in_path) { return fixup_backslashes_in_path(in_path.str()); } - -path& path::operator+=(path const& in) { - contents += in.contents; - return *this; -} -path& path::operator+=(std::string const& in) { - contents += in; - return *this; -} -path& path::operator+=(const char* in) { - contents += std::string{in}; - return *this; -} -path& path::operator/=(path const& in) { - if (contents.back() != path_separator && in.contents.front() != path_separator) contents += path_separator; - contents += in.contents; - return *this; -} -path& path::operator/=(std::string const& in) { - if (contents.back() != path_separator && in.front() != path_separator) contents += path_separator; - contents += in; - return *this; -} -path& path::operator/=(const char* in) { - std::string in_str{in}; - if (contents.back() != path_separator && in_str.front() != path_separator) contents += path_separator; - contents += in_str; - return *this; -} -path path::operator+(path const& in) const { - path new_path = contents; - new_path += in; - return new_path; -} -path path::operator+(std::string const& in) const { - path new_path = contents; - new_path += in; - return new_path; -} -path path::operator+(const char* in) const { - path new_path(contents); - new_path += in; - return new_path; -} - -path path::operator/(path const& in) const { - path new_path = contents; - new_path /= in; - return new_path; -} -path path::operator/(std::string const& in) const { - path new_path = contents; - new_path /= in; - return new_path; -} -path path::operator/(const char* in) const { - path new_path(contents); - new_path /= in; - return new_path; -} - -path path::parent_path() const { - auto last_div = contents.rfind(path_separator); - if (last_div == std::string::npos) return ""; - return path(contents.substr(0, last_div)); -} -bool path::has_parent_path() const { - auto last_div = contents.rfind(path_separator); - return last_div != std::string::npos; -} -path path::filename() const { - auto last_div = contents.rfind(path_separator); - return path(contents.substr(last_div + 1, contents.size() - last_div + 1)); -} - -path path::extension() const { - auto last_div = contents.rfind(path_separator); - auto ext_div = contents.rfind('.'); - // Make sure to not get the special `.` and `..`, as well as any filename that being with a dot, like .profile - if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) return path(""); - path temp = path(contents.substr(ext_div, contents.size() - ext_div + 1)); - - return path(contents.substr(ext_div, contents.size() - ext_div + 1)); -} - -path path::stem() const { - auto last_div = contents.rfind(path_separator); - auto ext_div = contents.rfind('.'); - if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) { - return path(contents.substr(last_div + 1, contents.size() - last_div + 1)); - } - return path(contents.substr(last_div + 1, ext_div - last_div - 1)); -} - -path& path::replace_filename(path const& replacement) { - *this = parent_path() / replacement.str(); - return *this; -} - // internal implementation helper for per-platform creating & destroying folders -int create_folder(path const& path) { +int create_folder(std::filesystem::path const& path) { #if defined(WIN32) - return _wmkdir(widen(path.str()).c_str()); + return _wmkdir(path.c_str()); #else mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); return 0; #endif } -int delete_folder_contents(path const& folder) { +int delete_folder_contents(std::filesystem::path const& folder) { #if defined(WIN32) - std::wstring folder_utf16 = widen(folder.str()); - if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(folder_utf16.c_str()) && GetLastError() == ERROR_FILE_NOT_FOUND) { + if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(folder.c_str()) && GetLastError() == ERROR_FILE_NOT_FOUND) { // nothing to delete return 0; } - std::wstring search_path = folder_utf16 + L"/*.*"; - std::string s_p = folder.str() + "/"; + std::filesystem::path search_path = folder / "*.*"; WIN32_FIND_DATAW fd; HANDLE hFind = ::FindFirstFileW(search_path.c_str(), &fd); if (hFind != INVALID_HANDLE_VALUE) { do { - std::string file_name_utf8 = narrow(fd.cFileName); + std::filesystem::path file_name = fd.cFileName; if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (!string_eq(file_name_utf8.c_str(), ".") && !string_eq(file_name_utf8.c_str(), "..")) { - delete_folder(s_p + file_name_utf8); + if (file_name != "." && file_name != "..") { + delete_folder(fd.cFileName); } } else { - std::string child_name = s_p + file_name_utf8; - DeleteFileW(widen(child_name).c_str()); + DeleteFileW((folder / fd.cFileName).native().c_str()); } } while (::FindNextFileW(hFind, &fd)); ::FindClose(hFind); @@ -388,7 +264,7 @@ int delete_folder_contents(path const& folder) { /* Skip the names "." and ".." as we don't want to recurse on them. */ if (string_eq(file->d_name, ".") || string_eq(file->d_name, "..")) continue; - path file_path = folder / file->d_name; + std::filesystem::path file_path = folder / file->d_name; struct stat statbuf; if (!stat(file_path.c_str(), &statbuf)) { if (S_ISDIR(statbuf.st_mode)) @@ -404,29 +280,23 @@ int delete_folder_contents(path const& folder) { #endif } -int delete_folder(path const& folder) { +int delete_folder(std::filesystem::path const& folder) { int ret = delete_folder_contents(folder); if (ret != 0) return ret; #if defined(WIN32) - _wrmdir(widen(folder.str()).c_str()); + _wrmdir(folder.native().c_str()); return 0; #else return rmdir(folder.c_str()); #endif } -#if defined(WIN32) -std::wstring native_path(const std::string& utf8) { return widen(utf8); } -#else -const std::string& native_path(const std::string& utf8) { return utf8; } -#endif - -FolderManager::FolderManager(path root_path, std::string name) noexcept : folder(root_path / name) { +FolderManager::FolderManager(std::filesystem::path root_path, std::string name) noexcept : folder(root_path / name) { delete_folder_contents(folder); create_folder(folder); } FolderManager::~FolderManager() noexcept { - if (folder.str().empty()) return; + if (folder.empty()) return; auto list_of_files_to_delete = files; // remove(file) modifies the files variable, copy the list before deleting it // Note: the allocation tests currently leak the loaded driver handles because in an OOM scenario the loader doesn't bother @@ -437,70 +307,72 @@ FolderManager::~FolderManager() noexcept { } delete_folder(folder); } -FolderManager::FolderManager(FolderManager&& other) noexcept : folder(other.folder), files(other.files) { - other.folder.str().clear(); -} +FolderManager::FolderManager(FolderManager&& other) noexcept : folder(other.folder), files(other.files) { other.folder.clear(); } FolderManager& FolderManager::operator=(FolderManager&& other) noexcept { folder = other.folder; files = other.files; - other.folder.str().clear(); + other.folder.clear(); return *this; } -path FolderManager::write_manifest(std::string const& name, std::string const& contents) { - path out_path = folder / name; +std::filesystem::path FolderManager::write_manifest(std::filesystem::path const& name, std::string const& contents) { + std::filesystem::path out_path = folder / name; auto found = std::find(files.begin(), files.end(), name); if (found != files.end()) { std::cout << "Overwriting manifest " << name << ". Was this intended?\n"; } else { files.emplace_back(name); } - auto file = std::ofstream(native_path(out_path.str()), std::ios_base::trunc | std::ios_base::out); + auto file = std::ofstream(out_path, std::ios_base::trunc | std::ios_base::out); if (!file) { - std::cerr << "Failed to create manifest " << name << " at " << out_path.str() << "\n"; + std::cerr << "Failed to create manifest " << name << " at " << out_path << "\n"; return out_path; } file << contents << std::endl; return out_path; } -void FolderManager::add_existing_file(std::string const& file_name) { files.emplace_back(file_name); } +void FolderManager::add_existing_file(std::filesystem::path const& file_name) { files.emplace_back(file_name); } // close file handle, delete file, remove `name` from managed file list. -void FolderManager::remove(std::string const& name) { - path out_path = folder / name; +void FolderManager::remove(std::filesystem::path const& name) { + std::filesystem::path out_path = folder / name; auto found = std::find(files.begin(), files.end(), name); if (found != files.end()) { +#if defined(WIN32) + int rc = _wremove(out_path.c_str()); +#else int rc = std::remove(out_path.c_str()); +#endif if (rc != 0) { - std::cerr << "Failed to remove file " << name << " at " << out_path.str() << "\n"; + std::cerr << "Failed to remove file " << name << " at " << out_path << "\n"; } files.erase(found); } else { - std::cout << "Couldn't remove file " << name << " at " << out_path.str() << ".\n"; + std::cout << "Couldn't remove file " << name << " at " << out_path << ".\n"; } } // copy file into this folder -path FolderManager::copy_file(path const& file, std::string const& new_name) { +std::filesystem::path FolderManager::copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name) { auto new_filepath = folder / new_name; auto found = std::find(files.begin(), files.end(), new_name); if (found != files.end()) { std::cout << "File location already contains" << new_name << ". Is this a bug?\n"; - } else if (file.str() == new_filepath.str()) { + } else if (file == new_filepath) { std::cout << "Trying to copy " << new_name << " into itself. Is this a bug?\n"; } else { files.emplace_back(new_name); } - std::ifstream src(native_path(file.str()), std::ios::binary); + std::ifstream src(file, std::ios::binary); if (!src) { - std::cerr << "Failed to create file " << file.str() << " for copying from\n"; + std::cerr << "Failed to create file " << file << " for copying from\n"; return new_filepath; } - std::ofstream dst(native_path(new_filepath.str()), std::ios::binary); + std::ofstream dst(new_filepath, std::ios::binary); if (!dst) { - std::cerr << "Failed to create file " << new_filepath.str() << " for copying to\n"; + std::cerr << "Failed to create file " << new_filepath << " for copying to\n"; return new_filepath; } dst << src.rdbuf(); @@ -660,4 +532,7 @@ std::wstring widen(const std::string& utf8) { } return utf16; } +#else +std::string narrow(const std::string& utf16) { return utf16; } +std::string widen(const std::string& utf8) { return utf8; } #endif diff --git a/tests/framework/test_util.h b/tests/framework/test_util.h index 1386151cd941694fc2631e0d2e8ad080b6536b20..38495ba92cf8be9c21caeb15a3c0676cc6145b09 100644 --- a/tests/framework/test_util.h +++ b/tests/framework/test_util.h @@ -31,7 +31,6 @@ * All the standard library includes and main platform specific includes * Dll export macro * Manifest ICD & Layer structs - * path abstraction class - modelled after C++17's filesystem::path * FolderManager - manages the contents of a folder, cleaning up when needed * per-platform library loading - mirrors the vk_loader_platform * LibraryWrapper - RAII wrapper for a library @@ -45,14 +44,11 @@ #include #include #include -#include #include #include #include #include -#include -#include -#include +#include #include #include @@ -90,7 +86,7 @@ #include #include -#include "framework_config.h" +#include FRAMEWORK_CONFIG_HEADER #if defined(__GNUC__) && __GNUC__ >= 4 #define FRAMEWORK_EXPORT __attribute__((visibility("default"))) @@ -102,6 +98,19 @@ #define FRAMEWORK_EXPORT #endif +// Define it here so that json_writer.h has access to these functions +#if defined(WIN32) +// Convert an UTF-16 wstring to an UTF-8 string +std::string narrow(const std::wstring& utf16); +// Convert an UTF-8 string to an UTF-16 wstring +std::wstring widen(const std::string& utf8); +#else +// Do nothing passthrough for the sake of Windows & UTF-16 +std::string narrow(const std::string& utf16); +// Do nothing passthrough for the sake of Windows & UTF-16 +std::string widen(const std::string& utf8); +#endif + #include "json_writer.h" // get_env_var() - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the @@ -149,6 +158,15 @@ struct EnvVarWrapper { cur_value += list_item; set_env_var(); } +#if defined(WIN32) + void add_to_list(std::wstring const& list_item) { + if (!cur_value.empty()) { + cur_value += OS_ENV_VAR_LIST_SEPARATOR; + } + cur_value += narrow(list_item); + set_env_var(); + } +#endif void remove_value() const { remove_env_var(); } const char* get() const { return name.c_str(); } const char* value() const { return cur_value.c_str(); } @@ -181,74 +199,13 @@ struct ManifestICD; // forward declaration for FolderManager::write struct ManifestLayer; // forward declaration for FolderManager::write namespace fs { -std::string make_native(std::string const&); -struct path { -#if defined(WIN32) - static const char path_separator = '\\'; -#elif COMMON_UNIX_PLATFORMS - static const char path_separator = '/'; -#endif - - public: - path() {} - path(std::string const& in) : contents(make_native(in)) {} - path(const char* in) : contents(make_native(std::string(in))) {} - - // concat paths without directoryseperator - path& operator+=(path const& in); - path& operator+=(std::string const& in); - path& operator+=(const char* in); - - // append paths with directoryseperator - path& operator/=(path const& in); - path& operator/=(std::string const& in); - path& operator/=(const char* in); - - // concat paths without directory seperator - path operator+(path const& in) const; - path operator+(std::string const& in) const; - path operator+(const char* in) const; - - // append paths with directory seperator - path operator/(path const& in) const; - path operator/(std::string const& in) const; - path operator/(const char* in) const; - - // accessors - path parent_path() const; // Everything before the last path separator, if there is one. - bool has_parent_path() const; // True if the path contains more than just a filename. - path filename() const; // Everything after the last path separator. - path extension() const; // The file extension, if it has one. - path stem() const; // The filename minus the extension. - - // modifiers - path& replace_filename(path const& replacement); - - // get c style string - const char* c_str() const { return contents.c_str(); } - // get C++ style string - std::string const& str() const { return contents; } - std::string& str() { return contents; } - size_t size() const { return contents.size(); } - - // equality - bool operator==(path const& other) const noexcept { return contents == other.contents; } - bool operator!=(path const& other) const noexcept { return !(*this == other); } - - private: - std::string contents; -}; - -std::string fixup_backslashes_in_path(std::string const& in_path); -fs::path fixup_backslashes_in_path(fs::path const& in_path); - -int create_folder(path const& path); -int delete_folder(path const& folder); +int create_folder(std::filesystem::path const& path); +int delete_folder(std::filesystem::path const& folder); class FolderManager { public: - explicit FolderManager(path root_path, std::string name) noexcept; + explicit FolderManager(std::filesystem::path root_path, std::string name) noexcept; ~FolderManager() noexcept; FolderManager(FolderManager const&) = delete; FolderManager& operator=(FolderManager const&) = delete; @@ -256,25 +213,25 @@ class FolderManager { FolderManager& operator=(FolderManager&& other) noexcept; // Add a manifest to the folder - path write_manifest(std::string const& name, std::string const& contents); + std::filesystem::path write_manifest(std::filesystem::path const& name, std::string const& contents); // Add an already existing file to the manager, so it will be cleaned up automatically - void add_existing_file(std::string const& file_name); + void add_existing_file(std::filesystem::path const& file_name); // close file handle, delete file, remove `name` from managed file list. - void remove(std::string const& name); + void remove(std::filesystem::path const& name); // copy file into this folder with name `new_name`. Returns the full path of the file that was copied - path copy_file(path const& file, std::string const& new_name); + std::filesystem::path copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name); // location of the managed folder - path location() const { return folder; } + std::filesystem::path location() const { return folder; } - std::vector get_files() const { return files; } + std::vector get_files() const { return files; } private: - path folder; - std::vector files; + std::filesystem::path folder; + std::vector files; }; } // namespace fs @@ -285,37 +242,26 @@ class FolderManager { inline void copy_string_to_char_array(std::string const& src, char* dst, size_t size_dst) { dst[src.copy(dst, size_dst - 1)] = 0; } #if defined(WIN32) -// Convert an UTF-16 wstring to an UTF-8 string -std::string narrow(const std::wstring& utf16); -// Convert an UTF-8 string to an UTF-16 wstring -std::wstring widen(const std::string& utf8); -#endif - -#if defined(WIN32) -typedef HMODULE loader_platform_dl_handle; -inline loader_platform_dl_handle loader_platform_open_library(const char* lib_path) { - std::wstring lib_path_utf16 = widen(lib_path); +typedef HMODULE test_platform_dl_handle; +inline test_platform_dl_handle test_platform_open_library(const wchar_t* lib_path) { // Try loading the library the original way first. - loader_platform_dl_handle lib_handle = LoadLibraryW(lib_path_utf16.c_str()); + test_platform_dl_handle lib_handle = LoadLibraryW(lib_path); if (lib_handle == nullptr && GetLastError() == ERROR_MOD_NOT_FOUND) { // If that failed, then try loading it with broader search folders. - lib_handle = - LoadLibraryExW(lib_path_utf16.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); + lib_handle = LoadLibraryExW(lib_path, nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); } return lib_handle; } -inline char* loader_platform_open_library_error(const char* libPath) { - static char errorMsg[164]; - snprintf(errorMsg, 163, "Failed to open dynamic library \"%s\" with error %lu", libPath, GetLastError()); - return errorMsg; +inline void test_platform_open_library_print_error(std::filesystem::path const& libPath) { + std::wcerr << L"Unable to open library: " << libPath << L" due to: " << std::to_wstring(GetLastError()) << L"\n"; } -inline void loader_platform_close_library(loader_platform_dl_handle library) { FreeLibrary(library); } -inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) { +inline void test_platform_close_library(test_platform_dl_handle library) { FreeLibrary(library); } +inline void* test_platform_get_proc_address(test_platform_dl_handle library, const char* name) { assert(library); assert(name); return reinterpret_cast(GetProcAddress(library, name)); } -inline char* loader_platform_get_proc_address_error(const char* name) { +inline char* test_platform_get_proc_address_error(const char* name) { static char errorMsg[120]; snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name); return errorMsg; @@ -323,18 +269,24 @@ inline char* loader_platform_get_proc_address_error(const char* name) { #elif COMMON_UNIX_PLATFORMS -typedef void* loader_platform_dl_handle; -inline loader_platform_dl_handle loader_platform_open_library(const char* libPath) { - return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL); +typedef void* test_platform_dl_handle; +inline test_platform_dl_handle test_platform_open_library(const char* libPath) { return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL); } +inline void test_platform_open_library_print_error(std::filesystem::path const& libPath) { + std::wcerr << "Unable to open library: " << libPath << " due to: " << dlerror() << "\n"; +} +inline void test_platform_close_library(test_platform_dl_handle library) { + char* loader_disable_dynamic_library_unloading_env_var = getenv("VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING"); + if (NULL == loader_disable_dynamic_library_unloading_env_var || + 0 != strncmp(loader_disable_dynamic_library_unloading_env_var, "1", 2)) { + dlclose(library); + } } -inline const char* loader_platform_open_library_error([[maybe_unused]] const char* libPath) { return dlerror(); } -inline void loader_platform_close_library(loader_platform_dl_handle library) { dlclose(library); } -inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) { +inline void* test_platform_get_proc_address(test_platform_dl_handle library, const char* name) { assert(library); assert(name); return dlsym(library, name); } -inline const char* loader_platform_get_proc_address_error([[maybe_unused]] const char* name) { return dlerror(); } +inline const char* test_platform_get_proc_address_error([[maybe_unused]] const char* name) { return dlerror(); } #endif class FromVoidStarFunc { @@ -353,17 +305,16 @@ class FromVoidStarFunc { struct LibraryWrapper { explicit LibraryWrapper() noexcept {} - explicit LibraryWrapper(fs::path const& lib_path) noexcept : lib_path(lib_path) { - lib_handle = loader_platform_open_library(lib_path.c_str()); + explicit LibraryWrapper(std::filesystem::path const& lib_path) noexcept : lib_path(lib_path) { + lib_handle = test_platform_open_library(lib_path.native().c_str()); if (lib_handle == nullptr) { - fprintf(stderr, "Unable to open library %s: %s\n", lib_path.c_str(), - loader_platform_open_library_error(lib_path.c_str())); + test_platform_open_library_print_error(lib_path); assert(lib_handle != nullptr && "Must be able to open library"); } } ~LibraryWrapper() noexcept { if (lib_handle != nullptr) { - loader_platform_close_library(lib_handle); + test_platform_close_library(lib_handle); lib_handle = nullptr; } } @@ -375,7 +326,7 @@ struct LibraryWrapper { LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept { if (this != &wrapper) { if (lib_handle != nullptr) { - loader_platform_close_library(lib_handle); + test_platform_close_library(lib_handle); } lib_handle = wrapper.lib_handle; lib_path = wrapper.lib_path; @@ -385,9 +336,9 @@ struct LibraryWrapper { } FromVoidStarFunc get_symbol(const char* symbol_name) const { assert(lib_handle != nullptr && "Cannot get symbol with null library handle"); - void* symbol = loader_platform_get_proc_address(lib_handle, symbol_name); + void* symbol = test_platform_get_proc_address(lib_handle, symbol_name); if (symbol == nullptr) { - fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, loader_platform_get_proc_address_error(symbol_name)); + fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, test_platform_get_proc_address_error(symbol_name)); assert(symbol != nullptr && "Must be able to get symbol"); } return FromVoidStarFunc(symbol); @@ -395,8 +346,8 @@ struct LibraryWrapper { explicit operator bool() const noexcept { return lib_handle != nullptr; } - loader_platform_dl_handle lib_handle = nullptr; - fs::path lib_path; + test_platform_dl_handle lib_handle = nullptr; + std::filesystem::path lib_path; }; template @@ -411,18 +362,20 @@ struct FRAMEWORK_EXPORT DispatchableHandle { handle = reinterpret_cast(ptr_handle); } ~DispatchableHandle() { - delete reinterpret_cast(handle); + if (handle) { + delete reinterpret_cast(handle); + } handle = nullptr; } DispatchableHandle(DispatchableHandle const&) = delete; DispatchableHandle& operator=(DispatchableHandle const&) = delete; DispatchableHandle(DispatchableHandle&& other) noexcept : handle(other.handle) { other.handle = nullptr; } DispatchableHandle& operator=(DispatchableHandle&& other) noexcept { - if (this != &other) { + if (handle) { delete reinterpret_cast(handle); - handle = other.handle; - other.handle = nullptr; } + handle = other.handle; + other.handle = nullptr; return *this; } bool operator==(T base_handle) { return base_handle == handle; } @@ -530,6 +483,10 @@ inline std::ostream& operator<<(std::ostream& os, const VkResult& result) { return os << "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR"; case (VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT): return os << "VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT"; + case (VK_PIPELINE_BINARY_MISSING_KHR): + return os << "VK_PIPELINE_BINARY_MISSING_KHR"; + case (VK_ERROR_NOT_ENOUGH_SPACE_KHR): + return os << "VK_ERROR_NOT_ENOUGH_SPACE_KHR"; } return os << static_cast(result); } @@ -595,7 +552,7 @@ struct ManifestVersion { struct ManifestICD { BUILDER_VALUE(ManifestICD, ManifestVersion, file_format_version, {}) BUILDER_VALUE(ManifestICD, uint32_t, api_version, 0) - BUILDER_VALUE(ManifestICD, fs::path, lib_path, {}) + BUILDER_VALUE(ManifestICD, std::filesystem::path, lib_path, {}) BUILDER_VALUE(ManifestICD, bool, is_portability_driver, false) BUILDER_VALUE(ManifestICD, std::string, library_arch, "") std::string get_manifest_str() const; @@ -630,7 +587,7 @@ struct ManifestLayer { }; BUILDER_VALUE(LayerDescription, std::string, name, {}) BUILDER_VALUE(LayerDescription, Type, type, Type::INSTANCE) - BUILDER_VALUE(LayerDescription, fs::path, lib_path, {}) + BUILDER_VALUE(LayerDescription, std::filesystem::path, lib_path, {}) BUILDER_VALUE(LayerDescription, uint32_t, api_version, VK_API_VERSION_1_0) BUILDER_VALUE(LayerDescription, uint32_t, implementation_version, 0) BUILDER_VALUE(LayerDescription, std::string, description, {}) @@ -641,7 +598,7 @@ struct ManifestLayer { BUILDER_VALUE(LayerDescription, std::string, disable_environment, {}) BUILDER_VECTOR(LayerDescription, std::string, component_layers, component_layer) BUILDER_VECTOR(LayerDescription, std::string, blacklisted_layers, blacklisted_layer) - BUILDER_VECTOR(LayerDescription, std::string, override_paths, override_path) + BUILDER_VECTOR(LayerDescription, std::filesystem::path, override_paths, override_path) BUILDER_VECTOR(LayerDescription, FunctionOverride, pre_instance_functions, pre_instance_function) BUILDER_VECTOR(LayerDescription, std::string, app_keys, app_key) BUILDER_VALUE(LayerDescription, std::string, library_arch, "") @@ -738,6 +695,9 @@ inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProp } inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); } +inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a == b.queueFamilyProperties; } +inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a != b.queueFamilyProperties; } + inline bool operator==(const VkLayerProperties& a, const VkLayerProperties& b) { return string_eq(a.layerName, b.layerName, 256) && string_eq(a.description, b.description, 256) && a.implementationVersion == b.implementationVersion && a.specVersion == b.specVersion; @@ -749,6 +709,170 @@ inline bool operator==(const VkExtensionProperties& a, const VkExtensionProperti } inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); } +inline bool operator==(const VkPhysicalDeviceFeatures& feats1, const VkPhysicalDeviceFeatures2& feats2) { + return feats1.robustBufferAccess == feats2.features.robustBufferAccess && + feats1.fullDrawIndexUint32 == feats2.features.fullDrawIndexUint32 && + feats1.imageCubeArray == feats2.features.imageCubeArray && feats1.independentBlend == feats2.features.independentBlend && + feats1.geometryShader == feats2.features.geometryShader && + feats1.tessellationShader == feats2.features.tessellationShader && + feats1.sampleRateShading == feats2.features.sampleRateShading && feats1.dualSrcBlend == feats2.features.dualSrcBlend && + feats1.logicOp == feats2.features.logicOp && feats1.multiDrawIndirect == feats2.features.multiDrawIndirect && + feats1.drawIndirectFirstInstance == feats2.features.drawIndirectFirstInstance && + feats1.depthClamp == feats2.features.depthClamp && feats1.depthBiasClamp == feats2.features.depthBiasClamp && + feats1.fillModeNonSolid == feats2.features.fillModeNonSolid && feats1.depthBounds == feats2.features.depthBounds && + feats1.wideLines == feats2.features.wideLines && feats1.largePoints == feats2.features.largePoints && + feats1.alphaToOne == feats2.features.alphaToOne && feats1.multiViewport == feats2.features.multiViewport && + feats1.samplerAnisotropy == feats2.features.samplerAnisotropy && + feats1.textureCompressionETC2 == feats2.features.textureCompressionETC2 && + feats1.textureCompressionASTC_LDR == feats2.features.textureCompressionASTC_LDR && + feats1.textureCompressionBC == feats2.features.textureCompressionBC && + feats1.occlusionQueryPrecise == feats2.features.occlusionQueryPrecise && + feats1.pipelineStatisticsQuery == feats2.features.pipelineStatisticsQuery && + feats1.vertexPipelineStoresAndAtomics == feats2.features.vertexPipelineStoresAndAtomics && + feats1.fragmentStoresAndAtomics == feats2.features.fragmentStoresAndAtomics && + feats1.shaderTessellationAndGeometryPointSize == feats2.features.shaderTessellationAndGeometryPointSize && + feats1.shaderImageGatherExtended == feats2.features.shaderImageGatherExtended && + feats1.shaderStorageImageExtendedFormats == feats2.features.shaderStorageImageExtendedFormats && + feats1.shaderStorageImageMultisample == feats2.features.shaderStorageImageMultisample && + feats1.shaderStorageImageReadWithoutFormat == feats2.features.shaderStorageImageReadWithoutFormat && + feats1.shaderStorageImageWriteWithoutFormat == feats2.features.shaderStorageImageWriteWithoutFormat && + feats1.shaderUniformBufferArrayDynamicIndexing == feats2.features.shaderUniformBufferArrayDynamicIndexing && + feats1.shaderSampledImageArrayDynamicIndexing == feats2.features.shaderSampledImageArrayDynamicIndexing && + feats1.shaderStorageBufferArrayDynamicIndexing == feats2.features.shaderStorageBufferArrayDynamicIndexing && + feats1.shaderStorageImageArrayDynamicIndexing == feats2.features.shaderStorageImageArrayDynamicIndexing && + feats1.shaderClipDistance == feats2.features.shaderClipDistance && + feats1.shaderCullDistance == feats2.features.shaderCullDistance && + feats1.shaderFloat64 == feats2.features.shaderFloat64 && feats1.shaderInt64 == feats2.features.shaderInt64 && + feats1.shaderInt16 == feats2.features.shaderInt16 && + feats1.shaderResourceResidency == feats2.features.shaderResourceResidency && + feats1.shaderResourceMinLod == feats2.features.shaderResourceMinLod && + feats1.sparseBinding == feats2.features.sparseBinding && + feats1.sparseResidencyBuffer == feats2.features.sparseResidencyBuffer && + feats1.sparseResidencyImage2D == feats2.features.sparseResidencyImage2D && + feats1.sparseResidencyImage3D == feats2.features.sparseResidencyImage3D && + feats1.sparseResidency2Samples == feats2.features.sparseResidency2Samples && + feats1.sparseResidency4Samples == feats2.features.sparseResidency4Samples && + feats1.sparseResidency8Samples == feats2.features.sparseResidency8Samples && + feats1.sparseResidency16Samples == feats2.features.sparseResidency16Samples && + feats1.sparseResidencyAliased == feats2.features.sparseResidencyAliased && + feats1.variableMultisampleRate == feats2.features.variableMultisampleRate && + feats1.inheritedQueries == feats2.features.inheritedQueries; +} + +inline bool operator==(const VkPhysicalDeviceMemoryProperties& props1, const VkPhysicalDeviceMemoryProperties2& props2) { + bool equal = true; + equal = equal && props1.memoryTypeCount == props2.memoryProperties.memoryTypeCount; + equal = equal && props1.memoryHeapCount == props2.memoryProperties.memoryHeapCount; + for (uint32_t i = 0; i < props1.memoryHeapCount; ++i) { + equal = equal && props1.memoryHeaps[i].size == props2.memoryProperties.memoryHeaps[i].size; + equal = equal && props1.memoryHeaps[i].flags == props2.memoryProperties.memoryHeaps[i].flags; + } + for (uint32_t i = 0; i < props1.memoryTypeCount; ++i) { + equal = equal && props1.memoryTypes[i].propertyFlags == props2.memoryProperties.memoryTypes[i].propertyFlags; + equal = equal && props1.memoryTypes[i].heapIndex == props2.memoryProperties.memoryTypes[i].heapIndex; + } + return equal; +} +inline bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties& props2) { + return props1.aspectMask == props2.aspectMask && props1.imageGranularity.width == props2.imageGranularity.width && + props1.imageGranularity.height == props2.imageGranularity.height && + props1.imageGranularity.depth == props2.imageGranularity.depth && props1.flags == props2.flags; +} +inline bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties2& props2) { + return props1 == props2.properties; +} +inline bool operator==(const VkExternalMemoryProperties& props1, const VkExternalMemoryProperties& props2) { + return props1.externalMemoryFeatures == props2.externalMemoryFeatures && + props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && + props1.compatibleHandleTypes == props2.compatibleHandleTypes; +} +inline bool operator==(const VkExternalSemaphoreProperties& props1, const VkExternalSemaphoreProperties& props2) { + return props1.externalSemaphoreFeatures == props2.externalSemaphoreFeatures && + props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && + props1.compatibleHandleTypes == props2.compatibleHandleTypes; +} +inline bool operator==(const VkExternalFenceProperties& props1, const VkExternalFenceProperties& props2) { + return props1.externalFenceFeatures == props2.externalFenceFeatures && + props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && + props1.compatibleHandleTypes == props2.compatibleHandleTypes; +} +inline bool operator==(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCapabilitiesKHR& props2) { + return props1.minImageCount == props2.minImageCount && props1.maxImageCount == props2.maxImageCount && + props1.currentExtent.width == props2.currentExtent.width && props1.currentExtent.height == props2.currentExtent.height && + props1.minImageExtent.width == props2.minImageExtent.width && + props1.minImageExtent.height == props2.minImageExtent.height && + props1.maxImageExtent.width == props2.maxImageExtent.width && + props1.maxImageExtent.height == props2.maxImageExtent.height && + props1.maxImageArrayLayers == props2.maxImageArrayLayers && props1.supportedTransforms == props2.supportedTransforms && + props1.currentTransform == props2.currentTransform && props1.supportedCompositeAlpha == props2.supportedCompositeAlpha && + props1.supportedUsageFlags == props2.supportedUsageFlags; +} +inline bool operator==(const VkSurfacePresentScalingCapabilitiesEXT& caps1, const VkSurfacePresentScalingCapabilitiesEXT& caps2) { + return caps1.supportedPresentScaling == caps2.supportedPresentScaling && + caps1.supportedPresentGravityX == caps2.supportedPresentGravityX && + caps1.supportedPresentGravityY == caps2.supportedPresentGravityY && + caps1.minScaledImageExtent.width == caps2.minScaledImageExtent.width && + caps1.minScaledImageExtent.height == caps2.minScaledImageExtent.height && + caps1.maxScaledImageExtent.width == caps2.maxScaledImageExtent.width && + caps1.maxScaledImageExtent.height == caps2.maxScaledImageExtent.height; +} +inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormatKHR& format2) { + return format1.format == format2.format && format1.colorSpace == format2.colorSpace; +} +inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormat2KHR& format2) { + return format1 == format2.surfaceFormat; +} +inline bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayPropertiesKHR& props2) { + return props1.display == props2.display && props1.physicalDimensions.width == props2.physicalDimensions.width && + props1.physicalDimensions.height == props2.physicalDimensions.height && + props1.physicalResolution.width == props2.physicalResolution.width && + props1.physicalResolution.height == props2.physicalResolution.height && + props1.supportedTransforms == props2.supportedTransforms && props1.planeReorderPossible == props2.planeReorderPossible && + props1.persistentContent == props2.persistentContent; +} +inline bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayProperties2KHR& props2) { + return props1 == props2.displayProperties; +} +inline bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModePropertiesKHR& disp2) { + return disp1.displayMode == disp2.displayMode && disp1.parameters.visibleRegion.width == disp2.parameters.visibleRegion.width && + disp1.parameters.visibleRegion.height == disp2.parameters.visibleRegion.height && + disp1.parameters.refreshRate == disp2.parameters.refreshRate; +} + +inline bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModeProperties2KHR& disp2) { + return disp1 == disp2.displayModeProperties; +} +inline bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilitiesKHR& caps2) { + return caps1.supportedAlpha == caps2.supportedAlpha && caps1.minSrcPosition.x == caps2.minSrcPosition.x && + caps1.minSrcPosition.y == caps2.minSrcPosition.y && caps1.maxSrcPosition.x == caps2.maxSrcPosition.x && + caps1.maxSrcPosition.y == caps2.maxSrcPosition.y && caps1.minSrcExtent.width == caps2.minSrcExtent.width && + caps1.minSrcExtent.height == caps2.minSrcExtent.height && caps1.maxSrcExtent.width == caps2.maxSrcExtent.width && + caps1.maxSrcExtent.height == caps2.maxSrcExtent.height && caps1.minDstPosition.x == caps2.minDstPosition.x && + caps1.minDstPosition.y == caps2.minDstPosition.y && caps1.maxDstPosition.x == caps2.maxDstPosition.x && + caps1.maxDstPosition.y == caps2.maxDstPosition.y && caps1.minDstExtent.width == caps2.minDstExtent.width && + caps1.minDstExtent.height == caps2.minDstExtent.height && caps1.maxDstExtent.width == caps2.maxDstExtent.width && + caps1.maxDstExtent.height == caps2.maxDstExtent.height; +} + +inline bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilities2KHR& caps2) { + return caps1 == caps2.capabilities; +} +inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlanePropertiesKHR& props2) { + return props1.currentDisplay == props2.currentDisplay && props1.currentStackIndex == props2.currentStackIndex; +} +inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlaneProperties2KHR& props2) { + return props1 == props2.displayPlaneProperties; +} +inline bool operator==(const VkExtent2D& ext1, const VkExtent2D& ext2) { + return ext1.height == ext2.height && ext1.width == ext2.width; +} +// Allow comparison of vectors of different types as long as their elements are comparable (just has to make sure to only apply when +// T != U) +template >> +bool operator==(const std::vector& a, const std::vector& b) { + return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](const auto& left, const auto& right) { return left == right; }); +} + struct VulkanFunction { std::string name; PFN_vkVoidFunction function = nullptr; @@ -838,7 +962,9 @@ inline std::string test_platform_executable_path() { inline std::string test_platform_executable_path() { return {}; } #elif defined(__QNX__) +#ifndef SYSCONFDIR #define SYSCONFDIR "/etc" +#endif #include #include @@ -847,14 +973,14 @@ inline std::string test_platform_executable_path() { std::string buffer; buffer.resize(1024); int fd = open("/proc/self/exefile", O_RDONLY); - size_t rdsize; + ssize_t rdsize; if (fd == -1) { return NULL; } rdsize = read(fd, &buffer[0], buffer.size()); - if (rdsize == size) { + if (rdsize < 0) { return NULL; } buffer[rdsize] = 0x00; @@ -873,15 +999,7 @@ inline std::string test_platform_executable_path() { if (ret > buffer.size()) return NULL; buffer.resize(ret); buffer[ret] = '\0'; - return buffer; -} - -inline std::wstring conver_str_to_wstr(std::string const& input) { - std::wstring output{}; - output.resize(input.size()); - size_t characters_converted = 0; - mbstowcs_s(&characters_converted, &output[0], output.size() + 1, input.c_str(), input.size()); - return output; + return narrow(std::filesystem::path(buffer).native()); } #endif diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index f15685222beb6df929a7f1e76308471da86e148e..e0f87e654a9b09d1a1f33782b4947c859326173a 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ~~~ -cmake_minimum_required(VERSION 3.17.2) +cmake_minimum_required(VERSION 3.22.1) project(INTEGRATION LANGUAGES C) diff --git a/tests/live_verification/CMakeLists.txt b/tests/live_verification/CMakeLists.txt index 3873c2dab53859194ec7e303d001cf0ef497cdfb..78e1f1aadc27d780c3eff13b8c145ee8e7a19d9e 100644 --- a/tests/live_verification/CMakeLists.txt +++ b/tests/live_verification/CMakeLists.txt @@ -28,3 +28,6 @@ if(APPLE_STATIC_LOADER) target_link_options(macos_static_loader_build PUBLIC -fsanitize=thread) endif() endif() + +add_executable(time_dynamic_loading time_dynamic_loading.cpp) +target_link_libraries(time_dynamic_loading Vulkan::Headers vulkan) diff --git a/tests/live_verification/time_dynamic_loading.cpp b/tests/live_verification/time_dynamic_loading.cpp new file mode 100644 index 0000000000000000000000000000000000000000..840afae1839bc00de9ed40ba670d70cba7072872 --- /dev/null +++ b/tests/live_verification/time_dynamic_loading.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 2023 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + * Author: Charles Giessen + */ + +#include + +#include +#include +#include +#include +#include +#include + +int main() { + // uint32_t iterations = 20; + // std::vector samples; + // samples.resize(iterations); + // for (uint32_t i = 0; i < iterations; i++) { + // auto t1 = std::chrono::system_clock::now(); + // uint32_t count = 0; + // vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr); + // // vkEnumerateInstanceLayerProperties(&count, nullptr); + // // vkEnumerateInstanceVersion(&count); + // auto t2 = std::chrono::system_clock::now(); + // samples[i] = std::chrono::duration_cast(t2 - t1); + // } + // std::chrono::microseconds total_time{}; + // for (uint32_t i = 0; i < iterations; i++) { + // total_time += samples[i]; + // } + // std::cout << "average time " << total_time.count() / iterations << " (μs)\n"; + // std::cout << std::setw(10) << "Iteration" << std::setw(12) << " Time (μs)\n"; + // for (uint32_t i = 0; i < iterations; i++) { + // std::cout << std::setw(10) << std::to_string(i) << std::setw(12) << samples[i].count() << "\n"; + // } + + uint32_t count = 0; + VkInstanceCreateInfo ci{}; + VkInstance i{}; + auto res = vkCreateInstance(&ci, nullptr, &i); + if (res != VK_SUCCESS) return -1; + std::cout << "After called vkCreateInstance\n"; + do { + std::cout << '\n' << "Press a key to continue..."; + } while (std::cin.get() != '\n'); + vkDestroyInstance(i, nullptr); + std::cout << "After called vkDestroyInstance\n"; + do { + std::cout << '\n' << "Press a key to continue..."; + } while (std::cin.get() != '\n'); +} diff --git a/tests/loader_alloc_callback_tests.cpp b/tests/loader_alloc_callback_tests.cpp index 1c31e06e49be22cde872709f0f276eba375d8447..1701bfb92793de0b0c83aa3a14010d34941272d7 100644 --- a/tests/loader_alloc_callback_tests.cpp +++ b/tests/loader_alloc_callback_tests.cpp @@ -27,6 +27,7 @@ #include "test_environment.h" +#include #include struct MemoryTrackerSettings { @@ -103,7 +104,10 @@ class MemoryTracker { void free(void* pMemory) { if (pMemory == nullptr) return; auto elem = allocations.find(pMemory); - if (elem == allocations.end()) return; + if (elem == allocations.end()) { + assert(false && "Should never be freeing memory that wasn't allocated by the MemoryTracker!"); + return; + } allocations.erase(elem); assert(allocation_count != 0 && "Cant free when there are no valid allocations"); allocation_count--; @@ -434,7 +438,8 @@ TEST(Allocation, CreateInstanceIntentionalAllocFailInvalidManifests) { for (size_t i = 0; i < invalid_jsons.size(); i++) { auto file_name = std::string("invalid_implicit_layer_") + std::to_string(i) + ".json"; - fs::path new_path = env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); + std::filesystem::path new_path = + env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); env.platform_shim->add_manifest(ManifestCategory::implicit_layer, new_path); } @@ -525,7 +530,7 @@ TEST(Allocation, CreateInstanceIntentionalAllocFailWithSettingsFilePresent) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("auto") - .set_path(env.get_shimmed_layer_manifest_path(0).str())))); + .set_path(env.get_shimmed_layer_manifest_path(0))))); size_t fail_index = 0; VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; @@ -561,7 +566,7 @@ TEST(Allocation, CreateSurfaceIntentionalAllocFailWithSettingsFilePresent) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("auto") - .set_path(env.get_shimmed_layer_manifest_path(0).str())))); + .set_path(env.get_shimmed_layer_manifest_path(0))))); size_t fail_index = 0; VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; @@ -608,7 +613,8 @@ TEST(Allocation, DriverEnvVarIntentionalAllocFail) { "test_layer.json"); env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); - env.env_var_vk_icd_filenames.add_to_list((fs::path("totally_made_up") / "path_to_fake" / "jason_file.json").str()); + env.env_var_vk_icd_filenames.add_to_list("totally_made_up/path_to_fake/jason_file.json"); + env.env_var_vk_icd_filenames.add_to_list("another\\bonkers\\file_path.json"); size_t fail_index = 0; VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { @@ -740,14 +746,9 @@ TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) { .set_disable_environment("DISABLE_ENV")), "test_layer_" + std::to_string(i) + ".json"); } - std::fstream custom_json_file{COMPLEX_JSON_FILE, std::ios_base::in}; - ASSERT_TRUE(custom_json_file.is_open()); - std::stringstream custom_json_file_contents; - custom_json_file_contents << custom_json_file.rdbuf(); - - fs::path new_path = env.get_folder(ManifestLocation::explicit_layer) - .write_manifest("VK_LAYER_complex_file.json", custom_json_file_contents.str()); - env.platform_shim->add_manifest(ManifestCategory::explicit_layer, new_path); + // Throw in a complex json file to flex the json allocation routines + env.write_file_from_source(COMPLEX_JSON_FILE, ManifestCategory::explicit_layer, ManifestLocation::explicit_layer, + "VK_LAYER_complex_file.json"); size_t fail_index = 0; VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; @@ -870,7 +871,7 @@ TEST(Allocation, EnumeratePhysicalDevicesIntentionalAllocFail) { size_t fail_index = 0; bool reached_the_end = false; uint32_t starting_physical_dev_count = 3; - while (!reached_the_end && fail_index <= 100) { + while (!reached_the_end && fail_index <= 10000) { fail_index++; // applies to the next loop uint32_t physical_dev_count = starting_physical_dev_count; VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; diff --git a/tests/loader_envvar_tests.cpp b/tests/loader_envvar_tests.cpp index 3bb7515e8bcef8947f65bb32784199344c9dfd52..c94c2ecdd4eb4846ce59610cd49952c2b7789f27 100644 --- a/tests/loader_envvar_tests.cpp +++ b/tests/loader_envvar_tests.cpp @@ -78,9 +78,9 @@ TEST(EnvVarICDOverrideSetup, version_2_negotiate_interface_version_and_icd_gipa) // export vk_icdNegotiateLoaderICDInterfaceVersion and vk_icdGetInstanceProcAddr TEST(EnvVarICDOverrideSetup, version_2_negotiate_interface_version_and_icd_gipa_unicode) { FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_UNICODE) + env.add_icd(TestICDDetails(widen(TEST_ICD_PATH_VERSION_2_UNICODE)) .set_discovery_type(ManifestDiscoveryType::env_var) - .set_json_name(TEST_JSON_NAME_VERSION_2_UNICODE)); + .set_json_name(widen(TEST_JSON_NAME_VERSION_2_UNICODE))); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); @@ -180,7 +180,7 @@ TEST(EnvVarICDOverrideSetup, TestOnlyDriverEnvVarInFolder) { env.platform_shim->set_elevated_privilege(false); } -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) // Make sure the loader reports the correct message based on if LOADER_USE_UNSAFE_FILE_SEARCH is set or not TEST(EnvVarICDOverrideSetup, NonSecureEnvVarLookup) { FrameworkEnvironment env{}; @@ -203,8 +203,8 @@ TEST(EnvVarICDOverrideSetup, XDG) { // Set up a layer path that includes default and user-specified locations, // so that the test app can find them. Include some badly specified elements as well. // Need to redirect the 'home' directory - fs::path HOME = "/home/fake_home"; - EnvVarWrapper home_env_var{"HOME", HOME.str()}; + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", ":/tmp/goober:::::/tmp/goober/::::"}; EnvVarWrapper xdg_config_home_env_var{"XDG_CONFIG_HOME", ":/tmp/goober:::::/tmp/goober2/::::"}; EnvVarWrapper xdg_data_dirs_env_var{"XDG_DATA_DIRS", "::::/tmp/goober3:/tmp/goober4/with spaces:::"}; @@ -218,10 +218,10 @@ TEST(EnvVarICDOverrideSetup, XDG) { inst.CheckCreate(); auto check_paths = [](DebugUtilsLogger const& debug_log, ManifestCategory category) { - EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober/vulkan") / category_path_name(category)).str())); - EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober2/vulkan") / category_path_name(category)).str())); - EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober3/vulkan") / category_path_name(category)).str())); - EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober4/with spaces/vulkan") / category_path_name(category)).str())); + EXPECT_TRUE(debug_log.find((std::filesystem::path("/tmp/goober/vulkan") / category_path_name(category)))); + EXPECT_TRUE(debug_log.find((std::filesystem::path("/tmp/goober2/vulkan") / category_path_name(category)))); + EXPECT_TRUE(debug_log.find((std::filesystem::path("/tmp/goober3/vulkan") / category_path_name(category)))); + EXPECT_TRUE(debug_log.find((std::filesystem::path("/tmp/goober4/with spaces/vulkan") / category_path_name(category)))); }; check_paths(env.debug_log, ManifestCategory::icd); check_paths(env.debug_log, ManifestCategory::implicit_layer); @@ -239,7 +239,7 @@ TEST(EnvVarICDOverrideSetup, XDGContainsJsonFile) { InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); } #endif @@ -290,7 +290,7 @@ TEST(EnvVarICDOverrideSetup, TestBothDriverEnvVars) { ASSERT_EQ(phys_dev_count, 3U); } -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) // Test VK_LAYER_PATH environment variable TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVar) { FrameworkEnvironment env{}; @@ -307,10 +307,10 @@ TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVar) { // Now set up a layer path that includes default and user-specified locations, // so that the test app can find them. Include some badly specified elements as well. // Need to redirect the 'home' directory - fs::path HOME = "/home/fake_home"; - EnvVarWrapper home_env_var{"HOME", HOME.str()}; + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; std::string vk_layer_path = ":/tmp/carol::::/:"; - vk_layer_path += (HOME / "/ with spaces/:::::/tandy:").str(); + vk_layer_path += (HOME / "/ with spaces/:::::/tandy:"); EnvVarWrapper layer_path_env_var{"VK_LAYER_PATH", vk_layer_path}; InstWrapper inst1{env.vulkan_functions}; inst1.create_info.add_layer(layer_name); @@ -320,7 +320,7 @@ TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVar) { // look for VK_LAYER_PATHS EXPECT_TRUE(env.debug_log.find("/tmp/carol")); EXPECT_TRUE(env.debug_log.find("/tandy")); - EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/").str())); + EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); env.debug_log.clear(); @@ -352,10 +352,10 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) { // Set up a layer path that includes default and user-specified locations, // so that the test app can find them. Include some badly specified elements as well. // Need to redirect the 'home' directory - fs::path HOME = "/home/fake_home"; - EnvVarWrapper home_env_var{"HOME", HOME.str()}; + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; std::string vk_layer_path = ":/tmp/carol::::/:"; - vk_layer_path += (HOME / "/ with spaces/:::::/tandy:").str(); + vk_layer_path += (HOME / "/ with spaces/:::::/tandy:"); EnvVarWrapper add_layer_path_env_var{"VK_ADD_LAYER_PATH", vk_layer_path}; InstWrapper inst1{env.vulkan_functions}; @@ -366,7 +366,7 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) { // look for VK_ADD_LAYER_PATHS EXPECT_TRUE(env.debug_log.find("/tmp/carol")); EXPECT_TRUE(env.debug_log.find("/tandy")); - EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/").str())); + EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); env.debug_log.clear(); @@ -382,6 +382,107 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) { env.platform_shim->set_elevated_privilege(false); } +// Test VK_IMPLICIT_LAYER_PATH environment variable +TEST(EnvVarICDOverrideSetup, TestOnlyImplicitLayerEnvVar) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); + env.platform_shim->redirect_path("/tmp/carol", env.get_folder(ManifestLocation::implicit_layer_env_var).location()); + + const char* layer_name = "TestLayer"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ENV")), + "test_layer.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); + + // Now set up a layer path that includes default and user-specified locations, + // so that the test app can find them. Include some badly specified elements as well. + // Need to redirect the 'home' directory + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; + std::string vk_implicit_layer_path = ":/tmp/carol::::/:"; + vk_implicit_layer_path += (HOME / "/ with spaces/:::::/tandy:"); + EnvVarWrapper implicit_layer_path_env_var{"VK_IMPLICIT_LAYER_PATH", vk_implicit_layer_path}; + InstWrapper inst1{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); + inst1.CheckCreate(); + + auto active_layers1 = inst1.GetActiveLayers(inst1.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(active_layers1.at(0).layerName, layer_name)); + + // look for VK_IMPLICIT_LAYER_PATHS + EXPECT_TRUE(env.debug_log.find("/tmp/carol")); + EXPECT_TRUE(env.debug_log.find("/tandy")); + EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); + + env.debug_log.clear(); + + env.platform_shim->set_elevated_privilege(true); + + InstWrapper inst2{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); + inst2.CheckCreate(); + + auto active_layers2 = inst2.GetActiveLayers(inst2.GetPhysDev(), 0); + ASSERT_TRUE(active_layers2.empty()); + + EXPECT_FALSE(env.debug_log.find("/tmp/carol")); + + env.platform_shim->set_elevated_privilege(false); +} + +// Test VK_ADD_IMPLICIT_LAYER_PATH environment variable +TEST(EnvVarICDOverrideSetup, TestOnlyAddImplicitLayerEnvVar) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); + env.platform_shim->redirect_path("/tmp/carol", env.get_folder(ManifestLocation::implicit_layer_add_env_var).location()); + + const char* layer_name = "TestLayer"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ENV")), + "test_layer.json") + .set_discovery_type(ManifestDiscoveryType::add_env_var)); + + // Set up a layer path that includes default and user-specified locations, + // so that the test app can find them. Include some badly specified elements as well. + // Need to redirect the 'home' directory + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; + std::string vk_add_implicit_layer_path = ":/tmp/carol::::/:"; + vk_add_implicit_layer_path += (HOME / "/ with spaces/:::::/tandy:"); + EnvVarWrapper add_implicit_layer_path_env_var{"VK_ADD_LAYER_PATH", vk_add_implicit_layer_path}; + + InstWrapper inst1{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); + inst1.CheckCreate(); + + auto active_layers1 = inst1.GetActiveLayers(inst1.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(active_layers1.at(0).layerName, layer_name)); + + // look for VK_ADD_IMPLICIT_LAYER_PATHS + EXPECT_TRUE(env.debug_log.find("/tmp/carol")); + EXPECT_TRUE(env.debug_log.find("/tandy")); + EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); + + env.debug_log.clear(); + + env.platform_shim->set_elevated_privilege(true); + + InstWrapper inst2{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); + inst2.CheckCreate(); + + auto active_layers2 = inst2.GetActiveLayers(inst2.GetPhysDev(), 0); + ASSERT_TRUE(active_layers2.empty()); + + EXPECT_FALSE(env.debug_log.find("/tmp/carol")); + + env.platform_shim->set_elevated_privilege(false); +} + #endif // Test that the driver filter select will only enable driver manifest files that match the filter diff --git a/tests/loader_fuzz_tests.cpp b/tests/loader_fuzz_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ac252cd2e2d45159574b576736734692e72715d --- /dev/null +++ b/tests/loader_fuzz_tests.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2024 The Khronos Group Inc. + * Copyright (c) 2024 Valve Corporation + * Copyright (c) 2024 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + * Author: Charles Giessen + */ + +#include "test_environment.h" + +extern "C" { +#include "loader.h" +#include "loader_json.h" +#include "settings.h" +} + +void execute_instance_enumerate_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::implicit_layer, ManifestLocation::implicit_layer, "complex_layer.json"); + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::settings, ManifestLocation::settings_location, "vk_loader_settings.json"); + + uint32_t pPropertyCount; + VkExtensionProperties pProperties = {0}; + + env.vulkan_functions.vkEnumerateInstanceExtensionProperties("test_auto", &pPropertyCount, &pProperties); +} +void execute_instance_create_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::implicit_layer, ManifestLocation::implicit_layer, "complex_layer.json"); + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::settings, ManifestLocation::settings_location, "vk_loader_settings.json"); + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::icd, ManifestLocation::driver, "icd_test.json"); + VkInstance inst = {0}; + const char* instance_layers[] = {"VK_LAYER_KHRONOS_validation", "VK_LAYER_test_layer_1", "VK_LAYER_test_layer_2"}; + VkApplicationInfo app{}; + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pNext = NULL; + app.pApplicationName = "TEST_APP"; + app.applicationVersion = 0; + app.pEngineName = "TEST_ENGINE"; + app.engineVersion = 0; + app.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo inst_info{}; + inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + inst_info.pNext = NULL; + inst_info.pApplicationInfo = &app; + inst_info.enabledLayerCount = 1; + inst_info.ppEnabledLayerNames = (const char* const*)instance_layers; + inst_info.enabledExtensionCount = 0; + inst_info.ppEnabledExtensionNames = NULL; + + VkResult err = env.vulkan_functions.vkCreateInstance(&inst_info, NULL, &inst); + if (err != VK_SUCCESS) { + return; + } + + env.vulkan_functions.vkDestroyInstance(inst, NULL); +} + +void execute_json_load_fuzzer(std::string const& filename) { + FrameworkEnvironment env{}; + + cJSON* json = NULL; + loader_get_json(NULL, filename.c_str(), &json); + + if (json == NULL) { + return; + } + bool out_of_mem = false; + char* json_data = loader_cJSON_Print(json, &out_of_mem); + + if (json_data != NULL) { + free(json_data); + } + + if (json != NULL) { + loader_cJSON_Delete(json); + } +} +void execute_setting_fuzzer(std::filesystem::path const& filename) { + FrameworkEnvironment env{}; + + env.write_file_from_source((std::filesystem::path(CLUSTERFUZZ_TESTCASE_DIRECTORY) / filename).string().c_str(), + ManifestCategory::settings, ManifestLocation::settings_location, "vk_loader_settings.json"); + + update_global_loader_settings(); + struct loader_layer_list settings_layers = {0}; + bool should_search_for_other_layers = true; + get_settings_layers(NULL, &settings_layers, &should_search_for_other_layers); + loader_delete_layer_list_and_properties(NULL, &settings_layers); +} + +TEST(BadJsonInput, ClusterFuzzTestCase_5599244505186304) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - instance_create_fuzzer: Abrt in loader_cJSON_Delete + execute_instance_create_fuzzer("clusterfuzz-testcase-instance_create_fuzzer-5599244505186304"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5126563864051712) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - instance_enumerate_fuzzer: Abrt in loader_cJSON_Delete + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-instance_enumerate_fuzzer-5126563864051712"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6308459683315712) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - instance_enumerate_fuzzer: Null-dereference READ in + // combine_settings_layers_with_regular_layers + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-instance_enumerate_fuzzer-6308459683315712"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6583684169269248) { + // Crashes ASAN + // Nullptr dereference in loader_copy_to_new_str + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6583684169269248"); +} + +TEST(BadJsonInput, ClusterFuzzTestCase_5258042868105216) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - json_load_fuzzer: Abrt in loader_cJSON_Delete + execute_json_load_fuzzer("clusterfuzz-testcase-json_load_fuzzer-5258042868105216"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5487817455960064) { + // Doesn't crash with ASAN or UBSAN + // Doesn't reproducibly crash - json_load_fuzzer: Abrt in std::__Fuzzer::vector, std::__ + execute_json_load_fuzzer("clusterfuzz-testcase-json_load_fuzzer-5487817455960064"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4558978302214144) { + // Does crash with UBSAN and ASAN + // loader.c:287: VkResult loader_copy_to_new_str(const struct loader_instance *, const char *, char **): Assertion + // `source_str + // && dest_str' failed. + // instance_create_fuzzer: Null-dereference READ in loader_copy_to_new_str + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-4558978302214144"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4568454561071104) { + // Does crash with UBSAN and ASAN + // Causes hangs - instance_create_fuzzer: Timeout in instance_create_fuzzer + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-4568454561071104"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4820577276723200) { + // Does crash with UBSAN and ASAN + // instance_create_fuzzer: Crash in printf_common + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-4820577276723200"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5177827962454016) { + // Does crash with UBSAN and ASAN + // free(): invalid next size (fast) + // instance_create_fuzzer: Abrt in instance_create_fuzzer + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5177827962454016"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5198773675425792) { + // Does crash with UBSAN and ASAN + // stack-overflow + // instance_create_fuzzer: Stack-overflow with empty stacktrace + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5198773675425792"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5416197367070720) { + // Does crash with UBSAN and ASAN + // free(): invalid next size (fast) + // instance_create_fuzzer: Overwrites-const-input in instance_create_fuzzer + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5416197367070720"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5494771615137792) { + // Does crash with UBSAN and ASAN + // stack-overflow + // instance_create_fuzzer: Stack-overflow in verify_meta_layer_component_layers + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5494771615137792"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5801855065915392) { + // Does crash with ASAN + // Doesn't crash with UBSAN + // Causes a leak - instance_create_fuzzer: Direct-leak in print_string_ptr + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-5801855065915392"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6353004288081920) { + // Does crash with ASAN and UBSAN + // Stack overflow due to recursive meta layers + execute_instance_create_fuzzer("clusterfuzz-testcase-minimized-instance_create_fuzzer-6353004288081920"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5817896795701248) { + execute_instance_create_fuzzer("clusterfuzz-testcase-instance_create_fuzzer-5817896795701248"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6541440380895232) { + execute_instance_create_fuzzer("clusterfuzz-testcase-instance_create_fuzzer-6541440380895232"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6465902356791296) { + // Does crash with UBSAN + // Doesn't crash with ASAN + // Causes an integer overflow - instance_enumerate_fuzzer: Integer-overflow in parse_value + execute_instance_enumerate_fuzzer("clusterfuzz-testcase-minimized-instance_enumerate_fuzzer-6465902356791296"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4512865114259456) { + // Does crash with UBSAN and ASAN + // malloc(): invalid size (unsorted) + // json_load_fuzzer: Heap-buffer-overflow in parse_string + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-4512865114259456"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4552015310880768) { + // Does crash with UBSAN + // Doesn't crash with ASAN + // Causes an integer overflow + // json_load_fuzzer: Integer-overflow in parse_value + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-4552015310880768"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5208693600747520) { + // Does crash with UBSAN and ASAN + // Stack overflow + // json_load_fuzzer: Stack-overflow in print_value + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5208693600747520"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5347670374612992) { + // Doesn't crash with ASAN or UBSAN + // No reported leaks in head, crashes in 1.3.269 & 1.3.250 + // Causes a leak - json_load_fuzzer: Direct-leak in parse_array + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5347670374612992"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5392928643547136) { + // Does crash with UBSAN and ASAN + // free(): corrupted unsorted chunks + // json_load_fuzzer: Abrt in std::__Fuzzer::basic_filebuf>::~basic_fil + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5392928643547136"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5636386303049728) { + // Does crash with UBSAN and ASAN + // terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc + // json_load_fuzzer: Abrt in json_load_fuzzer + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-5636386303049728"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6182254813249536) { + // Doesn't crash with ASAN or UBSAN + // No leaks reported in main, 1.3.269, nor 1.3.250 + // Causes a leak - json_load_fuzzer: Indirect-leak in parse_object + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6182254813249536"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6265355951996928) { + // Does crash with UBSAN and ASAN + // json_load_fuzzer: Null-dereference READ in json_load_fuzzer + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6265355951996928"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6363106126659584) { + // Does crash with UBSAN and ASAN + // json_load_fuzzer: Overwrites-const-input in json_load_fuzzer + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6363106126659584"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_6482033715838976) { + // Does crash with UBSAN and ASAN + // json_load_fuzzer: Stack-overflow in parse_array + execute_json_load_fuzzer("clusterfuzz-testcase-minimized-json_load_fuzzer-6482033715838976"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_4857714377818112) { + // Does crash with UBSAN and ASAN + // settings_fuzzer: Abrt in settings_fuzzer + execute_setting_fuzzer("clusterfuzz-testcase-minimized-settings_fuzzer-4857714377818112"); +} +TEST(BadJsonInput, ClusterFuzzTestCase_5123849246867456) { + // Doesn't crash with ASAN or UBSAN + // No leaks reported in main, 1.3.269, nor 1.3.250 + // Causes a leak - settings_fuzzer: Direct-leak in loader_append_layer_property + execute_setting_fuzzer("clusterfuzz-testcase-minimized-settings_fuzzer-5123849246867456"); +} diff --git a/tests/loader_get_proc_addr_tests.cpp b/tests/loader_get_proc_addr_tests.cpp index 295eee7b94bfb842b2d2f805d7a79c1293ce113f..a2fe578d8ea352ad4483478b9c24226edbdfdf15 100644 --- a/tests/loader_get_proc_addr_tests.cpp +++ b/tests/loader_get_proc_addr_tests.cpp @@ -213,6 +213,9 @@ TEST(GetDeviceProcAddr, SwapchainFuncsWithTerminator) { VkSurfaceKHR surface{}; ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + VkSurfaceKHR surface2{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface2)); + DebugUtilsWrapper log{inst}; ASSERT_EQ(VK_SUCCESS, CreateDebugUtilsMessenger(log)); auto phys_dev = inst.GetPhysDev(); @@ -236,25 +239,16 @@ TEST(GetDeviceProcAddr, SwapchainFuncsWithTerminator) { info.surface = surface; VkSwapchainKHR swapchain{}; - if (CreateSwapchainKHR) CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain); - ASSERT_FALSE( - log.find("vkCreateSwapchainKHR: Driver's function pointer was NULL, returning VK_SUCCESS. Was the VK_KHR_swapchain " - "extension enabled?")); log.logger.clear(); - if (dev_funcs.vkDestroySwapchainKHR) dev_funcs.vkDestroySwapchainKHR(dev.dev, swapchain, nullptr); - // try to call the vkCreateSwapchainKHR acquired from the instance - this *should* abort due to not enabling the extension - if (inst_CreateSwapchainKHR) { - ASSERT_DEATH(inst_CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain), - "vkCreateSwapchainKHR: Driver's function pointer was NULL, returning VK_SUCCESS. Was the VK_KHR_swapchain " - "extension enabled?"); - } - log.logger.clear(); - if (dev_funcs.vkDestroySwapchainKHR) dev_funcs.vkDestroySwapchainKHR(dev.dev, swapchain, nullptr); + ASSERT_FALSE(dev_funcs.vkDestroySwapchainKHR); - VkDeviceGroupPresentModeFlagsKHR modes{}; - if (GetDeviceGroupSurfacePresentModesKHR) GetDeviceGroupSurfacePresentModesKHR(dev.dev, surface, &modes); + // try to call the vkCreateSwapchainKHR acquired from the instance - this *should* abort due to not enabling the extension + ASSERT_DEATH(inst_CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain), + "vkCreateSwapchainKHR: Driver's function pointer was NULL, returning VK_SUCCESS. Was the VK_KHR_swapchain " + "extension enabled?"); - if (CreateSharedSwapchainsKHR) CreateSharedSwapchainsKHR(dev.dev, 1, &info, nullptr, &swapchain); + log.logger.clear(); + ASSERT_FALSE(dev_funcs.vkDestroySwapchainKHR); } driver.physical_devices.at(0).add_extensions({"VK_KHR_swapchain", "VK_KHR_display_swapchain", "VK_EXT_debug_marker"}); { @@ -272,31 +266,38 @@ TEST(GetDeviceProcAddr, SwapchainFuncsWithTerminator) { ASSERT_TRUE(inst_CreateSwapchainKHR); ASSERT_TRUE(GetDeviceGroupSurfacePresentModesKHR); ASSERT_TRUE(CreateSharedSwapchainsKHR); + ASSERT_TRUE(dev_funcs.vkDestroySwapchainKHR); VkSwapchainCreateInfoKHR info{}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.surface = surface; VkSwapchainKHR swapchain{}; - if (CreateSwapchainKHR) CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain); + CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain); ASSERT_FALSE( log.find("vkCreateSwapchainKHR: Driver's function pointer was NULL, returning VK_SUCCESS. Was the VK_KHR_swapchain " "extension enabled?")); log.logger.clear(); - if (dev_funcs.vkDestroySwapchainKHR) dev_funcs.vkDestroySwapchainKHR(dev.dev, swapchain, nullptr); - if (inst_CreateSwapchainKHR) inst_CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain); + dev_funcs.vkDestroySwapchainKHR(dev.dev, swapchain, nullptr); + inst_CreateSwapchainKHR(dev.dev, &info, nullptr, &swapchain); ASSERT_FALSE( log.find("vkCreateSwapchainKHR: Driver's function pointer was NULL, returning VK_SUCCESS. Was the VK_KHR_swapchain " "extension enabled?")); log.logger.clear(); - if (dev_funcs.vkDestroySwapchainKHR) dev_funcs.vkDestroySwapchainKHR(dev.dev, swapchain, nullptr); + dev_funcs.vkDestroySwapchainKHR(dev.dev, swapchain, nullptr); VkDeviceGroupPresentModeFlagsKHR modes{}; - if (GetDeviceGroupSurfacePresentModesKHR) GetDeviceGroupSurfacePresentModesKHR(dev.dev, surface, &modes); + GetDeviceGroupSurfacePresentModesKHR(dev.dev, surface, &modes); + + std::array infos{}; + infos[0] = info; + infos[1].sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + infos[1].surface = surface2; - if (CreateSharedSwapchainsKHR) CreateSharedSwapchainsKHR(dev.dev, 1, &info, nullptr, &swapchain); + ASSERT_EQ(VK_SUCCESS, CreateSharedSwapchainsKHR(dev.dev, 2, infos.data(), nullptr, &swapchain)); } env.vulkan_functions.vkDestroySurfaceKHR(inst.inst, surface, nullptr); + env.vulkan_functions.vkDestroySurfaceKHR(inst.inst, surface2, nullptr); } // Verify that the various ways to get vkGetDeviceProcAddr return the same value diff --git a/tests/loader_layer_tests.cpp b/tests/loader_layer_tests.cpp index b7c88171d7db943dbc5e2804a16609e599ed9224..156d8ee952d293bee093002a03769b7c28ab4d8f 100644 --- a/tests/loader_layer_tests.cpp +++ b/tests/loader_layer_tests.cpp @@ -31,7 +31,7 @@ void CheckLogForLayerString(FrameworkEnvironment& env, const char* implicit_laye { InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); if (check_for_enable) { ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name)); } else { @@ -119,7 +119,7 @@ TEST(ImplicitLayers, OnlyDisableEnvVar) { InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.create_info.add_layer(implicit_layer_name); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name)); } } @@ -962,7 +962,6 @@ TEST(ImplicitLayers, DuplicateLayers) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath const char* same_layer_name_1 = "VK_LAYER_RegularLayer1"; env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) @@ -988,7 +987,7 @@ TEST(ImplicitLayers, DuplicateLayers) { #if defined(WIN32) env.platform_shim->add_manifest(ManifestCategory::implicit_layer, env.get_folder(ManifestLocation::override_layer).location()); #elif COMMON_UNIX_PLATFORMS - env.platform_shim->redirect_path(fs::path(USER_LOCAL_SHARE_DIR "/vulkan/implicit_layer.d"), + env.platform_shim->redirect_path(std::filesystem::path(USER_LOCAL_SHARE_DIR "/vulkan/implicit_layer.d"), env.get_folder(ManifestLocation::override_layer).location()); #endif @@ -1009,6 +1008,177 @@ TEST(ImplicitLayers, DuplicateLayers) { ASSERT_FALSE(env.debug_log.find("actually_layer_2")); } +TEST(ImplicitLayers, VkImplicitLayerPathEnvVar) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + // verify layer loads successfully when setting VK_IMPLICIT_LAYER_PATH to a full filepath + const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name_1) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Yikes")), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(false)); + + InstWrapper inst(env.vulkan_functions); + inst.CheckCreate(); + auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name_1)); +} + +TEST(ImplicitLayers, VkImplicitLayerPathEnvVarContainsMultipleFilePaths) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + // verify layers load successfully when setting VK_IMPLICIT_LAYER_PATH to multiple full filepaths + const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name_1) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Yikes")), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(false)); + + const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name_2) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Yikes")), + "regular_layer_2.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(false)); + + InstWrapper inst(env.vulkan_functions); + inst.CheckCreate(); + auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name_1, regular_layer_name_2}, layer_props)); +} + +TEST(ImplicitLayers, VkImplicitLayerPathEnvVarIsDirectory) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + // verify layers load successfully when setting VK_IMPLICIT_LAYER_PATH to a directory + const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name_1) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Yikes")), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); + + const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name_2) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Yikes")), + "regular_layer_2.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); + + InstWrapper inst(env.vulkan_functions); + inst.CheckCreate(); + auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name_1, regular_layer_name_2}, layer_props)); +} + +// Test to make sure order layers are found in VK_IMPLICIT_LAYER_PATH is what decides which layer is loaded +TEST(ImplicitLayers, DuplicateLayersInVkImplicitLayerPath) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + const char* layer_name = "VK_LAYER_RegularLayer1"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_description("actually_layer_1") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Boo!")), + "layer.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(true)); + auto& layer1 = env.get_test_layer(0); + layer1.set_description("actually_layer_1"); + + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_description("actually_layer_2") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Ah!")), + "layer.json") + // putting it in a separate folder then manually adding the folder to VK_IMPLICIT_LAYER_PATH + .set_discovery_type(ManifestDiscoveryType::override_folder) + .set_is_dir(true)); + auto& layer2 = env.get_test_layer(1); + layer2.set_description("actually_layer_2"); + env.env_var_vk_implicit_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location().string()); + + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_name, layer_props[0].layerName)); + ASSERT_TRUE(string_eq(layer1.description.c_str(), layer_props[0].description)); + ASSERT_TRUE(string_eq(layer_name, layer_props[1].layerName)); + ASSERT_TRUE(string_eq(layer2.description.c_str(), layer_props[1].description)); + + EnvVarWrapper inst_layers_env_var{"VK_INSTANCE_LAYERS"}; + inst_layers_env_var.add_to_list(layer_name); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + // Expect the first layer added to be found + auto enabled_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layer_name, enabled_layer_props[0].layerName)); + ASSERT_TRUE(string_eq(layer1.description.c_str(), enabled_layer_props[0].description)); +} + +TEST(ImplicitLayers, DuplicateLayersInVK_ADD_IMPLICIT_LAYER_PATH) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + const char* same_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(same_layer_name_1) + .set_description("actually_layer_1") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Red")), + "regular_layer_1.json") + // use override folder as just a folder and manually set the VK_ADD_IMPLICIT_LAYER_PATH env-var to it + .set_discovery_type(ManifestDiscoveryType::override_folder) + .set_is_dir(true)); + auto& layer1 = env.get_test_layer(0); + layer1.set_description("actually_layer_1"); + layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); + env.add_env_var_vk_implicit_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location()); + + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(same_layer_name_1) + .set_description("actually_layer_2") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Blue")), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::add_env_var) + .set_is_dir(true)); + auto& layer2 = env.get_test_layer(1); + layer2.set_description("actually_layer_2"); + layer2.set_make_spurious_log_in_create_instance("actually_layer_2"); + + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(same_layer_name_1, layer_props[0].layerName)); + ASSERT_TRUE(string_eq(same_layer_name_1, layer_props[1].layerName)); + ASSERT_TRUE(string_eq(layer1.description.c_str(), layer_props[0].description)); + ASSERT_TRUE(string_eq(layer2.description.c_str(), layer_props[1].description)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + auto enabled_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(same_layer_name_1, enabled_layer_props.at(0).layerName)); + ASSERT_TRUE(string_eq(layer1.description.c_str(), enabled_layer_props.at(0).description)); + ASSERT_TRUE(env.debug_log.find("actually_layer_1")); + ASSERT_FALSE(env.debug_log.find("actually_layer_2")); +} + // Meta layer which contains component layers that do not exist. TEST(MetaLayers, InvalidComponentLayer) { FrameworkEnvironment env; @@ -1078,13 +1248,13 @@ TEST(MetaLayers, ExplicitMetaLayer) { } { // don't enable the layer, shouldn't find any layers when calling vkEnumerateDeviceLayerProperties InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } { InstWrapper inst{env.vulkan_functions}; inst.create_info.add_layer(meta_layer_name); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); EXPECT_TRUE(check_permutation({regular_layer_name, meta_layer_name}, layer_props)); } @@ -1512,14 +1682,14 @@ TEST(OverrideMetaLayer, BasicOverridePaths) { .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) .get_manifest_str()); - env.add_implicit_layer(ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer( - ManifestLayer::LayerDescription{} - .set_name(lunarg_meta_layer_name) - .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) - .add_component_layer(regular_layer_name) - .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(override_layer_folder.location().str()))), - "meta_test_layer.json"); + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(regular_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"); InstWrapper inst{env.vulkan_functions}; inst.create_info.set_api_version(1, 1, 0); @@ -1551,14 +1721,14 @@ TEST(OverrideMetaLayer, BasicOverridePathsIgnoreOtherLayers) { .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) .get_manifest_str()); - env.add_implicit_layer(ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer( - ManifestLayer::LayerDescription{} - .set_name(lunarg_meta_layer_name) - .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) - .add_component_layer(special_layer_name) - .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(override_layer_folder.location().str()))), - "meta_test_layer.json"); + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(special_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"); InstWrapper inst{env.vulkan_functions}; inst.create_info.set_api_version(1, 1, 0); @@ -1598,7 +1768,7 @@ TEST(OverrideMetaLayer, OverridePathsInteractionWithVK_LAYER_PATH) { .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) .add_component_layer(regular_layer_name) .set_disable_environment("DisableMeIfYouCan") - .add_override_path(env.get_folder(ManifestLocation::override_layer).location().str())), + .add_override_path(env.get_folder(ManifestLocation::override_layer).location())), "meta_test_layer.json"); auto meta_layer_path = env.get_folder(ManifestLocation::override_layer).location(); @@ -1613,7 +1783,7 @@ TEST(OverrideMetaLayer, OverridePathsInteractionWithVK_LAYER_PATH) { std::string("Ignoring VK_LAYER_PATH. The Override layer is active and has override paths set, which takes priority. " "VK_LAYER_PATH is set to ") + env.env_var_vk_layer_paths.value())); - ASSERT_TRUE(env.debug_log.find("Override layer has override paths set to " + meta_layer_path.str())); + ASSERT_TRUE(env.debug_log.find("Override layer has override paths set to " + meta_layer_path.string())); env.layers.clear(); } @@ -1646,7 +1816,7 @@ TEST(OverrideMetaLayer, OverridePathsEnableImplicitLayerInDefaultPaths) { .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) .add_component_layers({regular_layer_name, implicit_layer_name}) .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(override_layer_folder.location().str()))), + .add_override_path(override_layer_folder.location())), "meta_test_layer.json"); InstWrapper inst{env.vulkan_functions}; @@ -1672,21 +1842,22 @@ TEST(OverrideMetaLayer, ManifestFileFormatVersionTooOld) { .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) .get_manifest_str()); - env.add_implicit_layer(ManifestLayer{}.set_file_format_version({1, 0, 0}).add_layer( - ManifestLayer::LayerDescription{} - .set_name(lunarg_meta_layer_name) - .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) - .add_component_layer(regular_layer_name) - .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(override_layer_folder.location().str()))), - "meta_test_layer.json"); + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 0, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(regular_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"); InstWrapper inst{env.vulkan_functions}; inst.create_info.set_api_version(1, 1, 0); FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); - ASSERT_TRUE(env.debug_log.find("Indicating meta-layer-specific override paths, but using older JSON file version.")); + ASSERT_TRUE(env.debug_log.find(std::string("Layer \"") + lunarg_meta_layer_name + + "\" contains meta-layer-specific override paths, but using older JSON file version.")); env.layers.clear(); } @@ -1767,15 +1938,15 @@ TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromSecureLocation) { .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) .get_manifest_str()); - auto override_folder_location = fs::make_native(override_layer_folder.location().str()); - env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer( - ManifestLayer::LayerDescription{} - .set_name(lunarg_meta_layer_name) - .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) - .add_component_layer(regular_layer_name) - .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(override_layer_folder.location().str()))), - "meta_test_layer.json"}); + auto override_folder_location = override_layer_folder.location().string(); + env.add_implicit_layer(TestLayerDetails{ + ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(regular_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"}); { // try with no elevated privileges auto layer_props = env.GetLayerProperties(2); @@ -1822,14 +1993,14 @@ TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromUnsecureLocation) { .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) .get_manifest_str()); - env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer( - ManifestLayer::LayerDescription{} - .set_name(lunarg_meta_layer_name) - .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) - .add_component_layer(regular_layer_name) - .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(override_layer_folder.location().str()))), - "meta_test_layer.json"} + env.add_implicit_layer(TestLayerDetails{ + ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(regular_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::unsecured_generic)); { // try with no elevated privileges @@ -1939,7 +2110,7 @@ TEST(ExplicitLayers, LayerSettingsPreInstanceFunctions) { LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(false))); env.update_loader_settings(env.loader_settings); @@ -2011,6 +2182,29 @@ TEST(ExplicitLayers, ContainsPreInstanceFunctions) { ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name)); } +TEST(ExplicitLayers, CallsPreInstanceFunctionsInCreateInstance) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + const char* explicit_layer_name = "VK_LAYER_enabled_by_override"; + + env.add_explicit_layer( + ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer.json"); + + auto& layer = env.get_test_layer(0); + layer.set_query_vkEnumerateInstanceLayerProperties(true); + layer.set_query_vkEnumerateInstanceExtensionProperties(true); + layer.set_query_vkEnumerateInstanceVersion(true); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(explicit_layer_name); + inst.CheckCreate(); + + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name)); +} + // This test makes sure that any layer calling GetPhysicalDeviceProperties2 inside of CreateInstance // succeeds and doesn't crash. TEST(LayerCreateInstance, GetPhysicalDeviceProperties2) { @@ -2089,7 +2283,6 @@ TEST(ExplicitLayers, MultipleLayersInSingleManifest) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; const char* regular_layer_name_3 = "VK_LAYER_RegularLayer3"; @@ -2173,70 +2366,81 @@ TEST(ExplicitLayers, VkLayerPathEnvVar) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - { - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath - const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; - env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name(regular_layer_name_1) - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "regular_layer_1.json") - .set_discovery_type(ManifestDiscoveryType::env_var) - .set_is_dir(false)); - - InstWrapper inst(env.vulkan_functions); - inst.create_info.add_layer(regular_layer_name_1); - inst.CheckCreate(VK_SUCCESS); - auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); - EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name_1)); - } - { - // verify layers load successfully when setting VK_LAYER_PATH to multiple full filepaths - const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; - env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name(regular_layer_name_1) - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "regular_layer_1.json") - .set_discovery_type(ManifestDiscoveryType::env_var) - .set_is_dir(false)); - - const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; - env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name(regular_layer_name_2) - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "regular_layer_2.json") - .set_discovery_type(ManifestDiscoveryType::env_var) - .set_is_dir(false)); - - InstWrapper inst(env.vulkan_functions); - inst.create_info.add_layer(regular_layer_name_1); - inst.create_info.add_layer(regular_layer_name_2); - inst.CheckCreate(VK_SUCCESS); - auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); - EXPECT_TRUE(check_permutation({regular_layer_name_1, regular_layer_name_2}, layer_props)); - } - { - // verify layers load successfully when setting VK_LAYER_PATH to a directory - const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; - env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name(regular_layer_name_1) - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "regular_layer_1.json") - .set_discovery_type(ManifestDiscoveryType::env_var)); - - const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; - env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name(regular_layer_name_2) - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "regular_layer_2.json") - .set_discovery_type(ManifestDiscoveryType::env_var)); - - InstWrapper inst(env.vulkan_functions); - inst.create_info.add_layer(regular_layer_name_1); - inst.create_info.add_layer(regular_layer_name_2); - inst.CheckCreate(VK_SUCCESS); - auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); - EXPECT_TRUE(check_permutation({regular_layer_name_1, regular_layer_name_2}, layer_props)); - } + // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath + const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_explicit_layer( + TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_layer_name_1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(false)); + + InstWrapper inst(env.vulkan_functions); + inst.create_info.add_layer(regular_layer_name_1); + inst.CheckCreate(); + auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name_1)); +} + +TEST(ExplicitLayers, VkLayerPathEnvVarContainsMultipleFilepaths) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + // verify layers load successfully when setting VK_LAYER_PATH to multiple full filepaths + const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_explicit_layer( + TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_layer_name_1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(false)); + + const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; + env.add_explicit_layer( + TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_layer_name_2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_layer_2.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(false)); + + InstWrapper inst(env.vulkan_functions); + inst.create_info.add_layer(regular_layer_name_1); + inst.create_info.add_layer(regular_layer_name_2); + inst.CheckCreate(); + auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name_1, regular_layer_name_2}, layer_props)); +} + +TEST(ExplicitLayers, VkLayerPathEnvVarIsDirectory) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + // verify layers load successfully when setting VK_LAYER_PATH to a directory + const char* regular_layer_name_1 = "VK_LAYER_RegularLayer1"; + env.add_explicit_layer( + TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_layer_name_1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); + + const char* regular_layer_name_2 = "VK_LAYER_RegularLayer2"; + env.add_explicit_layer( + TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_layer_name_2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_layer_2.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); + + InstWrapper inst(env.vulkan_functions); + inst.create_info.add_layer(regular_layer_name_1); + inst.create_info.add_layer(regular_layer_name_2); + inst.CheckCreate(); + auto layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name_1, regular_layer_name_2}, layer_props)); } TEST(ExplicitLayers, DuplicateLayersInVK_LAYER_PATH) { @@ -2256,7 +2460,7 @@ TEST(ExplicitLayers, DuplicateLayersInVK_LAYER_PATH) { auto& layer1 = env.get_test_layer(0); layer1.set_description("actually_layer_1"); layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); - env.env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location().str()); + env.env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location()); env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) @@ -2318,7 +2522,6 @@ TEST(ExplicitLayers, DuplicateLayersInVK_ADD_LAYER_PATH) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath const char* same_layer_name_1 = "VK_LAYER_RegularLayer1"; env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) @@ -2331,7 +2534,7 @@ TEST(ExplicitLayers, DuplicateLayersInVK_ADD_LAYER_PATH) { auto& layer1 = env.get_test_layer(0); layer1.set_description("actually_layer_1"); layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); - env.add_env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location().str()); + env.add_env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location()); env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(same_layer_name_1) @@ -2393,7 +2596,6 @@ TEST(ExplicitLayers, CorrectOrderOfEnvVarEnabledLayers) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath const char* layer_name_1 = "VK_LAYER_RegularLayer1"; env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(layer_name_1) @@ -2454,12 +2656,56 @@ TEST(ExplicitLayers, CorrectOrderOfEnvVarEnabledLayers) { ASSERT_TRUE(string_eq(layer_name_1, enabled_layer_props[1].layerName)); } } +// Test to make sure order layers are found in VK_LAYER_PATH is what decides which layer is loaded +TEST(ExplicitLayers, DuplicateLayersInVkLayerPath) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + const char* layer_name = "VK_LAYER_RegularLayer1"; + env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_description("actually_layer_1") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "layer.json") + .set_discovery_type(ManifestDiscoveryType::env_var) + .set_is_dir(true)); + auto& layer1 = env.get_test_layer(0); + layer1.set_description("actually_layer_1"); + + env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_description("actually_layer_2") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "layer.json") + // putting it in a separate folder then manually adding the folder to VK_LAYER_PATH + .set_discovery_type(ManifestDiscoveryType::override_folder) + .set_is_dir(true)); + auto& layer2 = env.get_test_layer(1); + layer2.set_description("actually_layer_2"); + env.env_var_vk_layer_paths.add_to_list(env.get_folder(ManifestLocation::override_layer).location().string()); + + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_name, layer_props[0].layerName)); + ASSERT_TRUE(string_eq(layer1.description.c_str(), layer_props[0].description)); + ASSERT_TRUE(string_eq(layer_name, layer_props[1].layerName)); + ASSERT_TRUE(string_eq(layer2.description.c_str(), layer_props[1].description)); + + EnvVarWrapper inst_layers_env_var{"VK_INSTANCE_LAYERS"}; + inst_layers_env_var.add_to_list(layer_name); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + // Expect the first layer added to be found + auto enabled_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layer_name, enabled_layer_props[0].layerName)); + ASSERT_TRUE(string_eq(layer1.description.c_str(), enabled_layer_props[0].description)); +} TEST(ExplicitLayers, CorrectOrderOfEnvVarEnabledLayersFromSystemLocations) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath const char* layer_name_1 = "VK_LAYER_RegularLayer1"; env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(layer_name_1) @@ -2519,7 +2765,6 @@ TEST(ExplicitLayers, CorrectOrderOfApplicationEnabledLayers) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - // verify layer loads successfully when setting VK_LAYER_PATH to a full filepath const char* layer_name_1 = "VK_LAYER_RegularLayer1"; env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name(layer_name_1) @@ -5056,7 +5301,7 @@ TEST(TestLayers, AllowFilterWithExplicitLayer) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(false))); env.update_loader_settings(env.loader_settings); @@ -5138,7 +5383,7 @@ TEST(TestLayers, AllowFilterWithImplicitLayer) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); @@ -5210,7 +5455,7 @@ TEST(TestLayers, AllowFilterWithImplicitLayer) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); @@ -5290,7 +5535,7 @@ TEST(TestLayers, AllowFilterWithConditionallyImlicitLayer) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); @@ -5363,7 +5608,7 @@ TEST(TestLayers, AllowFilterWithConditionallyImlicitLayer) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); @@ -5408,15 +5653,14 @@ TEST(TestLayers, AllowFilterWithConditionallyImlicitLayerWithOverrideLayer) { "test_layer_all.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); - env.add_implicit_layer( - ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer( - ManifestLayer::LayerDescription{} - .set_name(lunarg_meta_layer_name) - .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) - .add_component_layer(layer_name) - .set_disable_environment("DisableMeIfYouCan") - .add_override_path(fs::make_native(env.get_folder(ManifestLocation::override_layer).location().str()))), - "meta_test_layer.json"); + env.add_implicit_layer(ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(env.get_folder(ManifestLocation::override_layer).location().string())), + "meta_test_layer.json"); EnvVarWrapper allow{"VK_LOADER_LAYERS_ALLOW", layer_name}; @@ -5463,7 +5707,7 @@ TEST(TestLayers, AllowFilterWithConditionallyImlicitLayerWithOverrideLayer) { LoaderSettingsLayerConfiguration{} .set_name(layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); diff --git a/tests/loader_phys_dev_inst_ext_tests.cpp b/tests/loader_phys_dev_inst_ext_tests.cpp index 36be527ef64caa10f0d0678459820010c23ccdd6..00c1eaf4c5979b9489471e18c2d958cec1577923 100644 --- a/tests/loader_phys_dev_inst_ext_tests.cpp +++ b/tests/loader_phys_dev_inst_ext_tests.cpp @@ -411,57 +411,6 @@ void FillInRandomFeatures(VkPhysicalDeviceFeatures& feats) { feats.inheritedQueries = (rand() % 2) == 0 ? VK_FALSE : VK_TRUE; } -// Compare the contents of the feature structs -bool CompareFeatures(const VkPhysicalDeviceFeatures& feats1, const VkPhysicalDeviceFeatures2& feats2) { - return feats1.robustBufferAccess == feats2.features.robustBufferAccess && - feats1.fullDrawIndexUint32 == feats2.features.fullDrawIndexUint32 && - feats1.imageCubeArray == feats2.features.imageCubeArray && feats1.independentBlend == feats2.features.independentBlend && - feats1.geometryShader == feats2.features.geometryShader && - feats1.tessellationShader == feats2.features.tessellationShader && - feats1.sampleRateShading == feats2.features.sampleRateShading && feats1.dualSrcBlend == feats2.features.dualSrcBlend && - feats1.logicOp == feats2.features.logicOp && feats1.multiDrawIndirect == feats2.features.multiDrawIndirect && - feats1.drawIndirectFirstInstance == feats2.features.drawIndirectFirstInstance && - feats1.depthClamp == feats2.features.depthClamp && feats1.depthBiasClamp == feats2.features.depthBiasClamp && - feats1.fillModeNonSolid == feats2.features.fillModeNonSolid && feats1.depthBounds == feats2.features.depthBounds && - feats1.wideLines == feats2.features.wideLines && feats1.largePoints == feats2.features.largePoints && - feats1.alphaToOne == feats2.features.alphaToOne && feats1.multiViewport == feats2.features.multiViewport && - feats1.samplerAnisotropy == feats2.features.samplerAnisotropy && - feats1.textureCompressionETC2 == feats2.features.textureCompressionETC2 && - feats1.textureCompressionASTC_LDR == feats2.features.textureCompressionASTC_LDR && - feats1.textureCompressionBC == feats2.features.textureCompressionBC && - feats1.occlusionQueryPrecise == feats2.features.occlusionQueryPrecise && - feats1.pipelineStatisticsQuery == feats2.features.pipelineStatisticsQuery && - feats1.vertexPipelineStoresAndAtomics == feats2.features.vertexPipelineStoresAndAtomics && - feats1.fragmentStoresAndAtomics == feats2.features.fragmentStoresAndAtomics && - feats1.shaderTessellationAndGeometryPointSize == feats2.features.shaderTessellationAndGeometryPointSize && - feats1.shaderImageGatherExtended == feats2.features.shaderImageGatherExtended && - feats1.shaderStorageImageExtendedFormats == feats2.features.shaderStorageImageExtendedFormats && - feats1.shaderStorageImageMultisample == feats2.features.shaderStorageImageMultisample && - feats1.shaderStorageImageReadWithoutFormat == feats2.features.shaderStorageImageReadWithoutFormat && - feats1.shaderStorageImageWriteWithoutFormat == feats2.features.shaderStorageImageWriteWithoutFormat && - feats1.shaderUniformBufferArrayDynamicIndexing == feats2.features.shaderUniformBufferArrayDynamicIndexing && - feats1.shaderSampledImageArrayDynamicIndexing == feats2.features.shaderSampledImageArrayDynamicIndexing && - feats1.shaderStorageBufferArrayDynamicIndexing == feats2.features.shaderStorageBufferArrayDynamicIndexing && - feats1.shaderStorageImageArrayDynamicIndexing == feats2.features.shaderStorageImageArrayDynamicIndexing && - feats1.shaderClipDistance == feats2.features.shaderClipDistance && - feats1.shaderCullDistance == feats2.features.shaderCullDistance && - feats1.shaderFloat64 == feats2.features.shaderFloat64 && feats1.shaderInt64 == feats2.features.shaderInt64 && - feats1.shaderInt16 == feats2.features.shaderInt16 && - feats1.shaderResourceResidency == feats2.features.shaderResourceResidency && - feats1.shaderResourceMinLod == feats2.features.shaderResourceMinLod && - feats1.sparseBinding == feats2.features.sparseBinding && - feats1.sparseResidencyBuffer == feats2.features.sparseResidencyBuffer && - feats1.sparseResidencyImage2D == feats2.features.sparseResidencyImage2D && - feats1.sparseResidencyImage3D == feats2.features.sparseResidencyImage3D && - feats1.sparseResidency2Samples == feats2.features.sparseResidency2Samples && - feats1.sparseResidency4Samples == feats2.features.sparseResidency4Samples && - feats1.sparseResidency8Samples == feats2.features.sparseResidency8Samples && - feats1.sparseResidency16Samples == feats2.features.sparseResidency16Samples && - feats1.sparseResidencyAliased == feats2.features.sparseResidencyAliased && - feats1.variableMultisampleRate == feats2.features.variableMultisampleRate && - feats1.inheritedQueries == feats2.features.inheritedQueries; -} - // Test vkGetPhysicalDeviceFeatures2KHR where nothing supports it. TEST(LoaderInstPhysDevExts, PhysDevFeats2KHRNoSupport) { FrameworkEnvironment env{}; @@ -513,7 +462,7 @@ TEST(LoaderInstPhysDevExts, PhysDevFeats2KHRInstanceAndICDSupport) { instance->vkGetPhysicalDeviceFeatures(physical_device, &feats); VkPhysicalDeviceFeatures2 feats2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR}; GetPhysDevFeats2KHR(physical_device, &feats2); - ASSERT_TRUE(CompareFeatures(feats, feats2)); + ASSERT_EQ(feats, feats2); } // Test vkGetPhysicalDeviceFeatures2 where instance supports, an ICD, and a device under that ICD @@ -545,7 +494,7 @@ TEST(LoaderInstPhysDevExts, PhysDevFeats2Simple) { instance->vkGetPhysicalDeviceFeatures(physical_device, &feats); VkPhysicalDeviceFeatures2 feats2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; GetPhysDevFeats2(physical_device, &feats2); - ASSERT_TRUE(CompareFeatures(feats, feats2)); + ASSERT_EQ(feats, feats2); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -566,7 +515,7 @@ TEST(LoaderInstPhysDevExts, PhysDevFeats2Simple) { instance->vkGetPhysicalDeviceFeatures(physical_device, &feats); VkPhysicalDeviceFeatures2 feats2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; GetPhysDevFeats2(physical_device, &feats2); - ASSERT_TRUE(CompareFeatures(feats, feats2)); + ASSERT_EQ(feats, feats2); ASSERT_TRUE(log.find("Emulating call in ICD")); } @@ -595,7 +544,7 @@ TEST(LoaderInstPhysDevExts, PhysDevFeats2Simple) { instance->vkGetPhysicalDeviceFeatures(physical_device, &feats); VkPhysicalDeviceFeatures2 feats2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; GetPhysDevFeats2(physical_device, &feats2); - ASSERT_TRUE(CompareFeatures(feats, feats2)); + ASSERT_EQ(feats, feats2); ASSERT_FALSE(log.find("Emulating call in ICD")); } @@ -635,11 +584,11 @@ TEST(LoaderInstPhysDevExts, PhysDevFeats2KHRInstanceSupports11) { VkPhysicalDeviceFeatures2KHR feats2KHR{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR}; GetPhysDevFeats2KHR(physical_device, &feats2KHR); - ASSERT_TRUE(CompareFeatures(feats, feats2KHR)); + ASSERT_EQ(feats, feats2KHR); VkPhysicalDeviceFeatures2 feats2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; GetPhysDevFeats2(physical_device, &feats2); - ASSERT_TRUE(CompareFeatures(feats, feats2)); + ASSERT_EQ(feats, feats2); ASSERT_FALSE(log.find("Emulating call in ICD")); } @@ -711,7 +660,7 @@ TEST(LoaderInstPhysDevExts, PhysDevFeatsMixed) { instance->vkGetPhysicalDeviceFeatures(physical_devices[dev], &feats); VkPhysicalDeviceFeatures2 feats2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; GetPhysDevFeats2(physical_devices[dev], &feats2); - ASSERT_TRUE(CompareFeatures(feats, feats2)); + ASSERT_EQ(feats, feats2); } } @@ -1439,22 +1388,6 @@ void FillInRandomMemoryData(VkPhysicalDeviceMemoryProperties& props) { } } -// Compare the memory structs -bool CompareMemoryData(const VkPhysicalDeviceMemoryProperties& props1, const VkPhysicalDeviceMemoryProperties2& props2) { - bool equal = true; - equal = equal && props1.memoryTypeCount == props2.memoryProperties.memoryTypeCount; - equal = equal && props1.memoryHeapCount == props2.memoryProperties.memoryHeapCount; - for (uint32_t i = 0; i < props1.memoryHeapCount; ++i) { - equal = equal && props1.memoryHeaps[i].size == props2.memoryProperties.memoryHeaps[i].size; - equal = equal && props1.memoryHeaps[i].flags == props2.memoryProperties.memoryHeaps[i].flags; - } - for (uint32_t i = 0; i < props1.memoryTypeCount; ++i) { - equal = equal && props1.memoryTypes[i].propertyFlags == props2.memoryProperties.memoryTypes[i].propertyFlags; - equal = equal && props1.memoryTypes[i].heapIndex == props2.memoryProperties.memoryTypes[i].heapIndex; - } - return equal; -} - // Test vkGetPhysicalDeviceMemoryProperties2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevMemoryProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -1481,7 +1414,7 @@ TEST(LoaderInstPhysDevExts, PhysDevMemoryProps2KHRInstanceAndICDSupport) { VkPhysicalDeviceMemoryProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2}; GetPhysDevMemoryProps2KHR(physical_device, &props2); - ASSERT_TRUE(CompareMemoryData(props, props2)); + ASSERT_EQ(props, props2); } // Test vkGetPhysicalDeviceMemoryProperties2 where instance supports, an ICD, and a device under that ICD @@ -1513,7 +1446,7 @@ TEST(LoaderInstPhysDevExts, PhysDevMemoryProps2Simple) { VkPhysicalDeviceMemoryProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2}; GetPhysDevMemoryProps2(physical_device, &props2); - ASSERT_TRUE(CompareMemoryData(props, props2)); + ASSERT_EQ(props, props2); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -1535,7 +1468,7 @@ TEST(LoaderInstPhysDevExts, PhysDevMemoryProps2Simple) { VkPhysicalDeviceMemoryProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2}; GetPhysDevMemoryProps2(physical_device, &props2); - ASSERT_TRUE(CompareMemoryData(props, props2)); + ASSERT_EQ(props, props2); ASSERT_TRUE(log.find("Emulating call in ICD")); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -1564,7 +1497,7 @@ TEST(LoaderInstPhysDevExts, PhysDevMemoryProps2Simple) { VkPhysicalDeviceMemoryProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2}; GetPhysDevMemoryProps2(physical_device, &props2); - ASSERT_TRUE(CompareMemoryData(props, props2)); + ASSERT_EQ(props, props2); ASSERT_FALSE(log.find("Emulating call in ICD")); } } @@ -1604,11 +1537,11 @@ TEST(LoaderInstPhysDevExts, PhysDevMemoryProps2KHRInstanceSupports11) { VkPhysicalDeviceMemoryProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2}; GetPhysDevMemoryProps2(physical_device, &props2); - ASSERT_TRUE(CompareMemoryData(props, props2)); + ASSERT_EQ(props, props2); VkPhysicalDeviceMemoryProperties2KHR props2KHR{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR}; GetPhysDevMemoryProps2KHR(physical_device, &props2KHR); - ASSERT_TRUE(CompareMemoryData(props, props2KHR)); + ASSERT_EQ(props, props2KHR); ASSERT_FALSE(log.find("Emulating call in ICD")); } @@ -1680,7 +1613,7 @@ TEST(LoaderInstPhysDevExts, PhysDevMemoryPropsMixed) { VkPhysicalDeviceMemoryProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2}; GetPhysDevMemoryProps2(physical_devices[dev], &props2); - ASSERT_TRUE(CompareMemoryData(props, props2)); + ASSERT_EQ(props, props2); } } @@ -1728,25 +1661,6 @@ uint32_t FillInRandomQueueFamilyData(std::vector& pro return static_cast(props.size()); } -// Compare the queue family structs -bool CompareQueueFamilyData(const std::vector& props1, - const std::vector& props2) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - equal = equal && props1[i].queueFlags == props2[i].queueFamilyProperties.queueFlags; - equal = equal && props1[i].queueCount == props2[i].queueFamilyProperties.queueCount; - equal = equal && props1[i].timestampValidBits == props2[i].queueFamilyProperties.timestampValidBits; - equal = equal && - props1[i].minImageTransferGranularity.width == props2[i].queueFamilyProperties.minImageTransferGranularity.width; - equal = equal && - props1[i].minImageTransferGranularity.height == props2[i].queueFamilyProperties.minImageTransferGranularity.height; - equal = equal && - props1[i].minImageTransferGranularity.depth == props2[i].queueFamilyProperties.minImageTransferGranularity.depth; - } - return equal; -} - // Test vkGetPhysicalDeviceQueueFamilyProperties2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -1782,7 +1696,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2KHRInstanceAndICDSupport) { ASSERT_EQ(ret_fam_1, ret_fam_2); props2.resize(ret_fam_2, VkQueueFamilyProperties2{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2}); GetPhysDevQueueFamilyProps2KHR(physical_device, &ret_fam_2, props2.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2)); + ASSERT_EQ(props, props2); } // Test vkGetPhysicalDeviceQueueFamilyProperties2 where instance supports, an ICD, and a device under that ICD @@ -1824,7 +1738,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2Simple) { ASSERT_EQ(ret_fam_1, ret_fam_2); props2.resize(ret_fam_2, VkQueueFamilyProperties2{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2}); GetPhysDevQueueFamilyProps2(physical_device, &ret_fam_2, props2.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2)); + ASSERT_EQ(props, props2); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -1856,7 +1770,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2Simple) { ASSERT_EQ(ret_fam_1, ret_fam_2); props2.resize(ret_fam_2, VkQueueFamilyProperties2{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2}); GetPhysDevQueueFamilyProps2(physical_device, &ret_fam_2, props2.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2)); + ASSERT_EQ(props, props2); ASSERT_TRUE(log.find("Emulating call in ICD")); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -1895,7 +1809,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2Simple) { ASSERT_EQ(ret_fam_1, ret_fam_2); props2.resize(ret_fam_2, VkQueueFamilyProperties2{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2}); GetPhysDevQueueFamilyProps2(physical_device, &ret_fam_2, props2.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2)); + ASSERT_EQ(props, props2); ASSERT_FALSE(log.find("Emulating call in ICD")); } } @@ -1945,7 +1859,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2KHRInstanceSupports11) { ASSERT_EQ(ret_fam_1, ret_fam_2); props2.resize(ret_fam_2, VkQueueFamilyProperties2{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2}); GetPhysDevQueueFamilyProps2(physical_device, &ret_fam_2, props2.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2)); + ASSERT_EQ(props, props2); std::vector props2KHR{}; uint32_t ret_fam_2_khr = 0; @@ -1953,7 +1867,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyProps2KHRInstanceSupports11) { ASSERT_EQ(ret_fam_1, ret_fam_2_khr); props2KHR.resize(ret_fam_2_khr, VkQueueFamilyProperties2KHR{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR}); GetPhysDevQueueFamilyProps2KHR(physical_device, &ret_fam_2_khr, props2KHR.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2KHR)); + ASSERT_EQ(props, props2KHR); ASSERT_FALSE(log.find("Emulating call in ICD")); } @@ -2034,7 +1948,7 @@ TEST(LoaderInstPhysDevExts, PhysDevQueueFamilyPropsMixed) { ASSERT_EQ(ret_fam_1, ret_fam_2); props2.resize(ret_fam_2, VkQueueFamilyProperties2{VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2}); GetPhysDevQueueFamilyProps2(physical_devices[dev], &ret_fam_2, props2.data()); - ASSERT_TRUE(CompareQueueFamilyData(props, props2)); + ASSERT_EQ(props, props2); } } @@ -2078,21 +1992,6 @@ void FillInRandomSparseImageFormatData(std::vector& props1, - const std::vector& props2) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - equal = equal && props1[i].aspectMask == props2[i].properties.aspectMask; - equal = equal && props1[i].imageGranularity.width == props2[i].properties.imageGranularity.width; - equal = equal && props1[i].imageGranularity.height == props2[i].properties.imageGranularity.height; - equal = equal && props1[i].imageGranularity.depth == props2[i].properties.imageGranularity.depth; - equal = equal && props1[i].flags == props2[i].properties.flags; - } - return equal; -} - // Test vkGetPhysicalDeviceSparseImageFormatProperties2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -2142,7 +2041,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2KHRInstanceAndICDSuppo props2.resize(sparse_count_2, VkSparseImageFormatProperties2{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2}); GetPhysDevSparseImageFormatProps2KHR(physical_device, &info2, &sparse_count_2, props2.data()); ASSERT_EQ(sparse_count_1, sparse_count_2); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2)); + ASSERT_EQ(props, props2); } // Test vkGetPhysicalDeviceSparseImageFormatProperties2 where instance supports, an ICD, and a device under that ICD @@ -2198,7 +2097,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2Simple) { props2.resize(sparse_count_2, VkSparseImageFormatProperties2{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2}); GetPhysDevSparseImageFormatProps2(physical_device, &info2, &sparse_count_2, props2.data()); ASSERT_EQ(sparse_count_1, sparse_count_2); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2)); + ASSERT_EQ(props, props2); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -2244,7 +2143,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2Simple) { props2.resize(sparse_count_2, VkSparseImageFormatProperties2{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2}); GetPhysDevSparseImageFormatProps2(physical_device, &info2, &sparse_count_2, props2.data()); ASSERT_EQ(sparse_count_1, sparse_count_2); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2)); + ASSERT_EQ(props, props2); ASSERT_TRUE(log.find("Emulating call in ICD")); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -2297,7 +2196,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2Simple) { props2.resize(sparse_count_2, VkSparseImageFormatProperties2{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2}); GetPhysDevSparseImageFormatProps2(physical_device, &info2, &sparse_count_2, props2.data()); ASSERT_EQ(sparse_count_1, sparse_count_2); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2)); + ASSERT_EQ(props, props2); ASSERT_FALSE(log.find("Emulating call in ICD")); } } @@ -2361,7 +2260,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2KHRInstanceSupports11) props2.resize(sparse_count_2, VkSparseImageFormatProperties2{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2}); GetPhysDevSparseImageFormatProps2(physical_device, &info2, &sparse_count_2, props2.data()); ASSERT_EQ(sparse_count_1, sparse_count_2); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2)); + ASSERT_EQ(props, props2); std::vector props2KHR{}; uint32_t sparse_count_2_khr = 0; @@ -2370,7 +2269,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatProps2KHRInstanceSupports11) props2KHR.resize(sparse_count_2, VkSparseImageFormatProperties2KHR{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR}); GetPhysDevSparseImageFormatProps2KHR(physical_device, &info2, &sparse_count_2_khr, props2KHR.data()); ASSERT_EQ(sparse_count_1, sparse_count_2_khr); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2KHR)); + ASSERT_EQ(props, props2KHR); ASSERT_FALSE(log.find("Emulating call in ICD")); } @@ -2467,7 +2366,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSparseImageFormatPropsMixed) { props2.resize(sparse_count_2, VkSparseImageFormatProperties2{VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2}); GetPhysDevSparseImageFormatProps2(physical_devices[dev], &info2, &sparse_count_2, props2.data()); ASSERT_EQ(sparse_count_1, sparse_count_2); - ASSERT_TRUE(CompareSparseImageFormatData(props, props2)); + ASSERT_EQ(props, props2); } } @@ -2511,22 +2410,6 @@ void FillInRandomExtMemoryData(VkExternalMemoryProperties& props) { props.compatibleHandleTypes = static_cast((rand() % 0x1FFE) + 1); } -// Compare the external memory data structs -bool CompareExtMemoryData(const VkExternalMemoryProperties& props1, const VkExternalMemoryProperties& props2, - bool supported = true) { - bool equal = true; - if (supported) { - equal = equal && props1.externalMemoryFeatures == props2.externalMemoryFeatures; - equal = equal && props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes; - equal = equal && props1.compatibleHandleTypes == props2.compatibleHandleTypes; - } else { - equal = equal && 0 == props2.externalMemoryFeatures; - equal = equal && 0 == props2.exportFromImportedHandleTypes; - equal = equal && 0 == props2.compatibleHandleTypes; - } - return equal; -} - // Test vkGetPhysicalDeviceExternalBufferPropertiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevExtBufProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -2551,8 +2434,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtBufProps2KHRInstanceAndICDSupport) { VkPhysicalDeviceExternalBufferInfoKHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR}; VkExternalBufferPropertiesKHR props{VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR}; GetPhysicalDeviceExternalBufferPropertiesKHR(physical_device, &info, &props); - ASSERT_TRUE(CompareExtMemoryData(env.get_test_icd(0).physical_devices.back().external_memory_properties, - props.externalMemoryProperties)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_memory_properties, props.externalMemoryProperties); } // Test vkGetPhysicalDeviceExternalBufferProperties where instance supports, an ICD, and a device under that ICD @@ -2583,8 +2465,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtBufProps2Simple) { VkPhysicalDeviceExternalBufferInfo info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO}; VkExternalBufferProperties props{VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES}; GetPhysicalDeviceExternalBufferProperties(physical_device, &info, &props); - ASSERT_TRUE(CompareExtMemoryData(env.get_test_icd(0).physical_devices.back().external_memory_properties, - props.externalMemoryProperties)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_memory_properties, props.externalMemoryProperties); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -2606,7 +2487,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtBufProps2Simple) { VkExternalBufferProperties props{VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES}; GetPhysicalDeviceExternalBufferProperties(physical_device, &info, &props); // Compare against 'zeroed' out VkExternalMemoryProperties - ASSERT_TRUE(CompareExtMemoryData(VkExternalMemoryProperties{}, props.externalMemoryProperties)); + ASSERT_EQ(VkExternalMemoryProperties{}, props.externalMemoryProperties); ASSERT_TRUE(log.find("Emulating call in ICD")); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -2634,8 +2515,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtBufProps2Simple) { VkPhysicalDeviceExternalBufferInfo info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO}; VkExternalBufferProperties props{VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES}; GetPhysicalDeviceExternalBufferProperties(physical_device, &info, &props); - ASSERT_TRUE(CompareExtMemoryData(env.get_test_icd(0).physical_devices.back().external_memory_properties, - props.externalMemoryProperties)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_memory_properties, props.externalMemoryProperties); ASSERT_FALSE(log.find("Emulating call in ICD")); } } @@ -2722,7 +2602,12 @@ TEST(LoaderInstPhysDevExts, PhysDevExtBufPropsMixed) { VkExternalBufferProperties props{VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES}; GetPhysicalDeviceExternalBufferProperties(physical_devices[dev], &info, &props); // No driver support for extension or 1.1 for ICD 1, all others support - ASSERT_TRUE(CompareExtMemoryData(cur_dev.external_memory_properties, props.externalMemoryProperties, icd != 1)); + if (icd != 1) { + ASSERT_EQ(cur_dev.external_memory_properties, props.externalMemoryProperties); + } else { + ASSERT_EQ(props.externalMemoryProperties, VkExternalMemoryProperties{}); + } + found = true; break; } @@ -2776,22 +2661,6 @@ void FillInRandomExtSemData(VkExternalSemaphoreProperties& props) { props.externalSemaphoreFeatures = static_cast((rand() % 0xFFF) + 1); } -// Compare the external semaphore data structs -bool CompareExtSemaphoreData(const VkExternalSemaphoreProperties& props1, const VkExternalSemaphoreProperties& props2, - bool supported = true) { - bool equal = true; - if (supported) { - equal = equal && props1.externalSemaphoreFeatures == props2.externalSemaphoreFeatures; - equal = equal && props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes; - equal = equal && props1.compatibleHandleTypes == props2.compatibleHandleTypes; - } else { - equal = equal && 0 == props2.externalSemaphoreFeatures; - equal = equal && 0 == props2.exportFromImportedHandleTypes; - equal = equal && 0 == props2.compatibleHandleTypes; - } - return equal; -} - // Test vkGetPhysicalDeviceExternalSemaphorePropertiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevExtSemProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -2816,7 +2685,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtSemProps2KHRInstanceAndICDSupport) { VkPhysicalDeviceExternalSemaphoreInfoKHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR}; VkExternalSemaphorePropertiesKHR props{VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR}; GetPhysicalDeviceExternalSemaphorePropertiesKHR(physical_device, &info, &props); - ASSERT_TRUE(CompareExtSemaphoreData(env.get_test_icd(0).physical_devices.back().external_semaphore_properties, props)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_semaphore_properties, props); } // Test vkGetPhysicalDeviceExternalSemaphoreProperties where instance supports, an ICD, and a device under that ICD @@ -2847,7 +2716,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtSemProps2Simple) { VkPhysicalDeviceExternalSemaphoreInfo info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO}; VkExternalSemaphoreProperties props{VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES}; GetPhysicalDeviceExternalSemaphoreProperties(physical_device, &info, &props); - ASSERT_TRUE(CompareExtSemaphoreData(env.get_test_icd(0).physical_devices.back().external_semaphore_properties, props)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_semaphore_properties, props); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -2868,7 +2737,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtSemProps2Simple) { VkExternalSemaphoreProperties props{VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES}; GetPhysicalDeviceExternalSemaphoreProperties(physical_device, &info, &props); // Compare against 'zeroed' out VkExternalSemaphoreProperties - ASSERT_TRUE(CompareExtSemaphoreData(VkExternalSemaphoreProperties{}, props)); + ASSERT_EQ(VkExternalSemaphoreProperties{}, props); ASSERT_TRUE(log.find("Emulating call in ICD")); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -2895,7 +2764,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtSemProps2Simple) { VkPhysicalDeviceExternalSemaphoreInfo info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO}; VkExternalSemaphoreProperties props{VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES}; GetPhysicalDeviceExternalSemaphoreProperties(physical_device, &info, &props); - ASSERT_TRUE(CompareExtSemaphoreData(env.get_test_icd(0).physical_devices.back().external_semaphore_properties, props)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_semaphore_properties, props); ASSERT_FALSE(log.find("Emulating call in ICD")); } } @@ -2982,7 +2851,11 @@ TEST(LoaderInstPhysDevExts, PhysDevExtSemPropsMixed) { VkExternalSemaphoreProperties props{VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES}; GetPhysicalDeviceExternalSemaphoreProperties(physical_devices[dev], &info, &props); // No driver support for extension or 1.1 for ICD 1, all others support - ASSERT_TRUE(CompareExtSemaphoreData(cur_dev.external_semaphore_properties, props, icd != 1)); + if (icd != 1) { + ASSERT_EQ(cur_dev.external_semaphore_properties, props); + } else { + ASSERT_EQ(props, VkExternalSemaphoreProperties{}); + } found = true; break; } @@ -3036,21 +2909,6 @@ void FillInRandomExtFenceData(VkExternalFenceProperties& props) { props.externalFenceFeatures = static_cast((rand() % 0xFFF) + 1); } -// Compare the external fence data structs -bool CompareExtFenceData(const VkExternalFenceProperties& props1, const VkExternalFenceProperties& props2, bool supported = true) { - bool equal = true; - if (supported) { - equal = equal && props1.externalFenceFeatures == props2.externalFenceFeatures; - equal = equal && props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes; - equal = equal && props1.compatibleHandleTypes == props2.compatibleHandleTypes; - } else { - equal = equal && 0 == props2.externalFenceFeatures; - equal = equal && 0 == props2.exportFromImportedHandleTypes; - equal = equal && 0 == props2.compatibleHandleTypes; - } - return equal; -} - // Test vkGetPhysicalDeviceExternalFencePropertiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevExtFenceProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -3075,7 +2933,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtFenceProps2KHRInstanceAndICDSupport) { VkPhysicalDeviceExternalFenceInfoKHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR}; VkExternalFencePropertiesKHR props{VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR}; GetPhysicalDeviceExternalFencePropertiesKHR(physical_device, &info, &props); - ASSERT_TRUE(CompareExtFenceData(env.get_test_icd(0).physical_devices.back().external_fence_properties, props)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_fence_properties, props); } // Test vkGetPhysicalDeviceExternalFenceProperties where instance supports, an ICD, and a device under that ICD @@ -3106,7 +2964,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtFenceProps2Simple) { VkPhysicalDeviceExternalFenceInfo info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO}; VkExternalFenceProperties props{VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES}; GetPhysicalDeviceExternalFenceProperties(physical_device, &info, &props); - ASSERT_TRUE(CompareExtFenceData(env.get_test_icd(0).physical_devices.back().external_fence_properties, props)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_fence_properties, props); } { // Now do the same logic but the application didn't enable 1.0 or the extension so they get the emulated call InstWrapper instance(env.vulkan_functions); @@ -3128,7 +2986,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtFenceProps2Simple) { VkExternalFenceProperties props{VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES}; GetPhysicalDeviceExternalFenceProperties(physical_device, &info, &props); // Compare against 'zeroed' out VkExternalFenceProperties - ASSERT_TRUE(CompareExtFenceData(VkExternalFenceProperties{}, props)); + ASSERT_EQ(VkExternalFenceProperties{}, props); ASSERT_TRUE(log.find("Emulating call in ICD")); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -3155,7 +3013,7 @@ TEST(LoaderInstPhysDevExts, PhysDevExtFenceProps2Simple) { VkPhysicalDeviceExternalFenceInfo info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO}; VkExternalFenceProperties props{VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES}; GetPhysicalDeviceExternalFenceProperties(physical_device, &info, &props); - ASSERT_TRUE(CompareExtFenceData(env.get_test_icd(0).physical_devices.back().external_fence_properties, props)); + ASSERT_EQ(env.get_test_icd(0).physical_devices.back().external_fence_properties, props); ASSERT_FALSE(log.find("Emulating call in ICD")); } } @@ -3241,7 +3099,11 @@ TEST(LoaderInstPhysDevExts, PhysDevExtFencePropsMixed) { VkExternalFenceProperties props{VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES}; GetPhysicalDeviceExternalFenceProperties(physical_devices[dev], &info, &props); // No driver support for extension or 1.1 for ICD 1, all others support - ASSERT_TRUE(CompareExtFenceData(cur_dev.external_fence_properties, props, icd != 1)); + if (icd != 1) { + ASSERT_EQ(cur_dev.external_fence_properties, props); + } else { + ASSERT_EQ(props, VkExternalFenceProperties{}); + } found = true; break; } @@ -3303,41 +3165,6 @@ void FillInRandomSurfaceCapsData(VkSurfaceCapabilitiesKHR& props) { props.supportedUsageFlags = static_cast((rand() % 0xFFF) + 1); } -// Compare the surface capability data structs -bool CompareSurfaceCapsData(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCapabilitiesKHR& props2, bool supported = true) { - bool equal = true; - if (supported) { - equal = equal && props1.minImageCount == props2.minImageCount; - equal = equal && props1.maxImageCount == props2.maxImageCount; - equal = equal && props1.currentExtent.width == props2.currentExtent.width; - equal = equal && props1.currentExtent.height == props2.currentExtent.height; - equal = equal && props1.minImageExtent.width == props2.minImageExtent.width; - equal = equal && props1.minImageExtent.height == props2.minImageExtent.height; - equal = equal && props1.maxImageExtent.width == props2.maxImageExtent.width; - equal = equal && props1.maxImageExtent.height == props2.maxImageExtent.height; - equal = equal && props1.maxImageArrayLayers == props2.maxImageArrayLayers; - equal = equal && props1.supportedTransforms == props2.supportedTransforms; - equal = equal && props1.currentTransform == props2.currentTransform; - equal = equal && props1.supportedCompositeAlpha == props2.supportedCompositeAlpha; - equal = equal && props1.supportedUsageFlags == props2.supportedUsageFlags; - } else { - equal = equal && 0 == props2.minImageCount; - equal = equal && 0 == props2.maxImageCount; - equal = equal && 0 == props2.currentExtent.width; - equal = equal && 0 == props2.currentExtent.height; - equal = equal && 0 == props2.minImageExtent.width; - equal = equal && 0 == props2.minImageExtent.height; - equal = equal && 0 == props2.maxImageExtent.width; - equal = equal && 0 == props2.maxImageExtent.height; - equal = equal && 0 == props2.maxImageArrayLayers; - equal = equal && 0 == props2.supportedTransforms; - equal = equal && 0 == props2.currentTransform; - equal = equal && 0 == props2.supportedCompositeAlpha; - equal = equal && 0 == props2.supportedUsageFlags; - } - return equal; -} - // Test vkGetPhysicalDeviceSurfaceCapabilities2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevSurfaceCaps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -3383,7 +3210,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSurfaceCaps2KHRInstanceAndICDSupport) { VkPhysicalDeviceSurfaceInfo2KHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr, surface}; VkSurfaceCapabilities2KHR props2{VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR}; ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceCapabilities2KHR(physical_device, &info, &props2)); - ASSERT_TRUE(CompareSurfaceCapsData(props, props2.surfaceCapabilities)); + ASSERT_EQ(props, props2.surfaceCapabilities); DestroySurfaceKHR(instance.inst, surface, nullptr); } @@ -3479,7 +3306,7 @@ TEST(LoaderInstPhysDevExts, PhysDevSurfaceCaps2KHRMixed) { VkPhysicalDeviceSurfaceInfo2KHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr, surface}; VkSurfaceCapabilities2KHR props2{VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR}; ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceCapabilities2KHR(physical_devices[dev], &info, &props2)); - ASSERT_TRUE(CompareSurfaceCapsData(props, props2.surfaceCapabilities)); + ASSERT_EQ(props, props2.surfaceCapabilities); } DestroySurfaceKHR(instance.inst, surface, nullptr); @@ -3523,23 +3350,6 @@ void FillInRandomSurfaceFormatsData(std::vector& props) { } } -// Compare the surface formats data structs -bool CompareSurfaceFormatsData(const std::vector& props1, const std::vector& props2, - bool supported = true) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - if (supported) { - equal = equal && props1[i].format == props2[i].surfaceFormat.format; - equal = equal && props1[i].colorSpace == props2[i].surfaceFormat.colorSpace; - } else { - equal = equal && 0 == props2[i].surfaceFormat.format; - equal = equal && 0 == props2[i].surfaceFormat.colorSpace; - } - } - return equal; -} - // Test vkGetPhysicalDeviceSurfaceFormats2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevSurfaceFormats2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -3579,23 +3389,22 @@ TEST(LoaderInstPhysDevExts, PhysDevSurfaceFormats2KHRInstanceAndICDSupport) { VkHeadlessSurfaceCreateInfoEXT create_info{VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT}; ASSERT_EQ(VK_SUCCESS, CreateHeadlessSurfaceEXT(instance.inst, &create_info, nullptr, &surface)); - std::vector props{}; + std::vector formats{}; uint32_t count_1 = 0; ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &count_1, nullptr)); ASSERT_EQ(env.get_test_icd(0).physical_devices.back().surface_formats.size(), count_1); - props.resize(count_1); - ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &count_1, props.data())); + formats.resize(count_1); + ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &count_1, formats.data())); ASSERT_EQ(env.get_test_icd(0).physical_devices.back().surface_formats.size(), count_1); VkPhysicalDeviceSurfaceInfo2KHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr, surface}; - std::vector props2{}; + std::vector formats2{}; uint32_t count_2 = 0; ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormats2KHR(physical_device, &info, &count_2, nullptr)); ASSERT_EQ(count_1, count_2); - props2.resize(count_2, VkSurfaceFormat2KHR{VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}); - ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormats2KHR(physical_device, &info, &count_2, props2.data())); - ASSERT_TRUE(CompareSurfaceFormatsData(props, props2)); - + formats2.resize(count_2, VkSurfaceFormat2KHR{VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}); + ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormats2KHR(physical_device, &info, &count_2, formats2.data())); + ASSERT_EQ(formats, formats2); DestroySurfaceKHR(instance.inst, surface, nullptr); } @@ -3684,23 +3493,23 @@ TEST(LoaderInstPhysDevExts, PhysDevSurfaceFormats2KHRMixed) { ASSERT_EQ(VK_SUCCESS, CreateHeadlessSurfaceEXT(instance.inst, &create_info, nullptr, &surface)); for (uint32_t dev = 0; dev < device_count; ++dev) { - std::vector props{}; + std::vector formats{}; uint32_t count_1 = 0; ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormatsKHR(physical_devices[dev], surface, &count_1, nullptr)); ASSERT_NE(0U, count_1); - props.resize(count_1); - ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormatsKHR(physical_devices[dev], surface, &count_1, props.data())); + formats.resize(count_1); + ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormatsKHR(physical_devices[dev], surface, &count_1, formats.data())); ASSERT_NE(0U, count_1); VkPhysicalDeviceSurfaceInfo2KHR info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr, surface}; - std::vector props2{}; + std::vector formats2{}; uint32_t count_2 = 0; ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormats2KHR(physical_devices[dev], &info, &count_2, nullptr)); ASSERT_EQ(count_1, count_2); - props2.resize(count_2, VkSurfaceFormat2KHR{VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}); - ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormats2KHR(physical_devices[dev], &info, &count_2, props2.data())); + formats2.resize(count_2, VkSurfaceFormat2KHR{VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}); + ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceFormats2KHR(physical_devices[dev], &info, &count_2, formats2.data())); ASSERT_EQ(count_1, count_2); - ASSERT_TRUE(CompareSurfaceFormatsData(props, props2)); + ASSERT_EQ(formats, formats2); } DestroySurfaceKHR(instance.inst, surface, nullptr); @@ -3760,23 +3569,6 @@ void FillInRandomDisplayPropData(std::vector& props) { } } -// Compare the display property data structs -bool CompareDisplayPropData(const std::vector& props1, const std::vector& props2) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - equal = equal && props1[i].display == props2[i].display; - equal = equal && props1[i].physicalDimensions.width == props2[i].physicalDimensions.width; - equal = equal && props1[i].physicalDimensions.height == props2[i].physicalDimensions.height; - equal = equal && props1[i].physicalResolution.width == props2[i].physicalResolution.width; - equal = equal && props1[i].physicalResolution.height == props2[i].physicalResolution.height; - equal = equal && props1[i].supportedTransforms == props2[i].supportedTransforms; - equal = equal && props1[i].planeReorderPossible == props2[i].planeReorderPossible; - equal = equal && props1[i].persistentContent == props2[i].persistentContent; - } - return equal; -} - // Test vGetPhysicalDeviceDisplayPropertiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevDispPropsKHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -3806,7 +3598,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPropsKHRInstanceAndICDSupport) { ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceDisplayPropertiesKHR(physical_device, &prop_count, props.data())); ASSERT_EQ(env.get_test_icd(0).physical_devices.back().display_properties.size(), prop_count); - ASSERT_TRUE(CompareDisplayPropData(props, env.get_test_icd(0).physical_devices.back().display_properties)); + ASSERT_EQ(props, env.get_test_icd(0).physical_devices.back().display_properties); } // Test vkGetPhysicalDeviceDisplayPropertiesKHR where instance supports it with some ICDs that both support @@ -3902,7 +3694,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPropsKHRMixed) { GetPhysicalDeviceDisplayPropertiesKHR(physical_devices[dev], &prop_count, props.data())); ASSERT_EQ(cur_dev.display_properties.size(), prop_count); - ASSERT_TRUE(CompareDisplayPropData(props, cur_dev.display_properties)); + ASSERT_EQ(props, cur_dev.display_properties); } found = true; break; @@ -3953,18 +3745,6 @@ void FillInRandomDisplayPlanePropData(std::vector& } } -// Compare the display plane property data structs -bool CompareDisplayPlanePropData(const std::vector& props1, - const std::vector& props2) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - equal = equal && props1[i].currentDisplay == props2[i].currentDisplay; - equal = equal && props1[i].currentStackIndex == props2[i].currentStackIndex; - } - return equal; -} - // Test vGetPhysicalDeviceDisplayPlanePropertiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevDispPlanePropsKHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -3994,7 +3774,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPlanePropsKHRInstanceAndICDSupport) { ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceDisplayPlanePropertiesKHR(physical_device, &prop_count, props.data())); ASSERT_EQ(env.get_test_icd(0).physical_devices.back().display_plane_properties.size(), prop_count); - ASSERT_TRUE(CompareDisplayPlanePropData(props, env.get_test_icd(0).physical_devices.back().display_plane_properties)); + ASSERT_EQ(props, env.get_test_icd(0).physical_devices.back().display_plane_properties); } // Test vkGetPhysicalDeviceDisplayPlanePropertiesKHR where instance supports it with some ICDs that both support @@ -4090,7 +3870,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPlanePropsKHRMixed) { GetPhysicalDeviceDisplayPlanePropertiesKHR(physical_devices[dev], &prop_count, props.data())); ASSERT_EQ(cur_dev.display_plane_properties.size(), prop_count); - ASSERT_TRUE(CompareDisplayPlanePropData(props, cur_dev.display_plane_properties)); + ASSERT_EQ(props, cur_dev.display_plane_properties); } found = true; break; @@ -4140,16 +3920,6 @@ void GenerateRandomDisplays(std::vector& disps) { } } -// Compare the display plane property data structs -bool CompareDisplays(const std::vector& disps1, const std::vector& disps2) { - if (disps1.size() != disps2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < disps1.size(); ++i) { - equal = equal && disps1[i] == disps2[i]; - } - return equal; -} - // Test vGetDisplayPlaneSupportedDisplaysKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, GetDispPlaneSupDispsKHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -4179,7 +3949,7 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneSupDispsKHRInstanceAndICDSupport) { ASSERT_EQ(VK_SUCCESS, GetDisplayPlaneSupportedDisplaysKHR(physical_device, 0, &disp_count, disps.data())); ASSERT_EQ(env.get_test_icd(0).physical_devices.back().displays.size(), disp_count); - ASSERT_TRUE(CompareDisplays(disps, env.get_test_icd(0).physical_devices.back().displays)); + ASSERT_EQ(disps, env.get_test_icd(0).physical_devices.back().displays); } // Test vkGetDisplayPlaneSupportedDisplaysKHR where instance supports it with some ICDs that both support @@ -4275,7 +4045,7 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneSupDispsKHRMixed) { GetDisplayPlaneSupportedDisplaysKHR(physical_devices[dev], 0, &disp_count, disps.data())); ASSERT_EQ(cur_dev.displays.size(), disp_count); - ASSERT_TRUE(CompareDisplays(disps, cur_dev.displays)); + ASSERT_EQ(disps, cur_dev.displays); } found = true; break; @@ -4326,20 +4096,6 @@ void GenerateRandomDisplayModeProps(std::vector& dis } } -// Compare the display mode properties data structs -bool CompareDisplayModeProps(const std::vector& disps1, - const std::vector& disps2) { - if (disps1.size() != disps2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < disps1.size(); ++i) { - equal = equal && disps1[i].displayMode == disps2[i].displayMode; - equal = equal && disps1[i].parameters.visibleRegion.width == disps2[i].parameters.visibleRegion.width; - equal = equal && disps1[i].parameters.visibleRegion.height == disps2[i].parameters.visibleRegion.height; - equal = equal && disps1[i].parameters.refreshRate == disps2[i].parameters.refreshRate; - } - return equal; -} - // Test vGetDisplayModePropertiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, GetDispModePropsKHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -4368,7 +4124,7 @@ TEST(LoaderInstPhysDevExts, GetDispModePropsKHRInstanceAndICDSupport) { ASSERT_EQ(VK_SUCCESS, GetDisplayModePropertiesKHR(physical_device, VK_NULL_HANDLE, &props_count, props.data())); ASSERT_EQ(env.get_test_icd(0).physical_devices.back().display_mode_properties.size(), props_count); - ASSERT_TRUE(CompareDisplayModeProps(props, env.get_test_icd(0).physical_devices.back().display_mode_properties)); + ASSERT_EQ(props, env.get_test_icd(0).physical_devices.back().display_mode_properties); } // Test vkGetDisplayModePropertiesKHR where instance supports it with some ICDs that both support @@ -4464,7 +4220,7 @@ TEST(LoaderInstPhysDevExts, GetDispModePropsKHRMixed) { GetDisplayModePropertiesKHR(physical_devices[dev], VK_NULL_HANDLE, &props_count, props.data())); ASSERT_EQ(cur_dev.display_mode_properties.size(), props_count); - ASSERT_TRUE(CompareDisplayModeProps(props, cur_dev.display_mode_properties)); + ASSERT_EQ(props, cur_dev.display_mode_properties); } found = true; break; @@ -4504,9 +4260,6 @@ TEST(LoaderInstPhysDevExts, GetDispModesKHRNoICDSupport) { ASSERT_EQ(CreateDisplayModeKHR, nullptr); } -// Compare the display modes -bool CompareDisplayModes(const VkDisplayModeKHR& disps1, VkDisplayModeKHR& disps2) { return disps1 == disps2; } - // Test vkCreateDisplayModeKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, GetDispModesKHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -4530,7 +4283,7 @@ TEST(LoaderInstPhysDevExts, GetDispModesKHRInstanceAndICDSupport) { VkDisplayModeKHR mode{}; VkDisplayModeCreateInfoKHR create_info{VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR}; ASSERT_EQ(VK_SUCCESS, CreateDisplayModeKHR(physical_device, VK_NULL_HANDLE, &create_info, nullptr, &mode)); - ASSERT_TRUE(CompareDisplayModes(mode, env.get_test_icd(0).physical_devices.back().display_mode)); + ASSERT_EQ(mode, env.get_test_icd(0).physical_devices.back().display_mode); } // Test vkCreateDisplayModeKHR where instance supports it with some ICDs that both support @@ -4620,7 +4373,7 @@ TEST(LoaderInstPhysDevExts, GetDispModesKHRMixed) { } else { ASSERT_EQ(VK_SUCCESS, CreateDisplayModeKHR(physical_devices[dev], VK_NULL_HANDLE, &create_info, nullptr, &mode)); - ASSERT_TRUE(CompareDisplayModes(mode, cur_dev.display_mode)); + ASSERT_EQ(mode, cur_dev.display_mode); } found = true; break; @@ -4681,50 +4434,6 @@ void GenerateRandomDisplayPlaneCaps(VkDisplayPlaneCapabilitiesKHR& caps) { caps.maxDstExtent.height = static_cast((rand() % 0xFFFFFFF) + 1); } -// Compare the display plane caps -bool CompareDisplayPlaneCaps(const VkDisplayPlaneCapabilitiesKHR& caps1, VkDisplayPlaneCapabilitiesKHR& caps2, - bool supported = true) { - bool equal = true; - if (supported) { - equal = equal && caps1.supportedAlpha == caps2.supportedAlpha; - equal = equal && caps1.minSrcPosition.x == caps2.minSrcPosition.x; - equal = equal && caps1.minSrcPosition.y == caps2.minSrcPosition.y; - equal = equal && caps1.maxSrcPosition.x == caps2.maxSrcPosition.x; - equal = equal && caps1.maxSrcPosition.y == caps2.maxSrcPosition.y; - equal = equal && caps1.minSrcExtent.width == caps2.minSrcExtent.width; - equal = equal && caps1.minSrcExtent.height == caps2.minSrcExtent.height; - equal = equal && caps1.maxSrcExtent.width == caps2.maxSrcExtent.width; - equal = equal && caps1.maxSrcExtent.height == caps2.maxSrcExtent.height; - equal = equal && caps1.minDstPosition.x == caps2.minDstPosition.x; - equal = equal && caps1.minDstPosition.y == caps2.minDstPosition.y; - equal = equal && caps1.maxDstPosition.x == caps2.maxDstPosition.x; - equal = equal && caps1.maxDstPosition.y == caps2.maxDstPosition.y; - equal = equal && caps1.minDstExtent.width == caps2.minDstExtent.width; - equal = equal && caps1.minDstExtent.height == caps2.minDstExtent.height; - equal = equal && caps1.maxDstExtent.width == caps2.maxDstExtent.width; - equal = equal && caps1.maxDstExtent.height == caps2.maxDstExtent.height; - } else { - equal = equal && caps1.supportedAlpha == 0; - equal = equal && caps1.minSrcPosition.x == 0; - equal = equal && caps1.minSrcPosition.y == 0; - equal = equal && caps1.maxSrcPosition.x == 0; - equal = equal && caps1.maxSrcPosition.y == 0; - equal = equal && caps1.minSrcExtent.width == 0; - equal = equal && caps1.minSrcExtent.height == 0; - equal = equal && caps1.maxSrcExtent.width == 0; - equal = equal && caps1.maxSrcExtent.height == 0; - equal = equal && caps1.minDstPosition.x == 0; - equal = equal && caps1.minDstPosition.y == 0; - equal = equal && caps1.maxDstPosition.x == 0; - equal = equal && caps1.maxDstPosition.y == 0; - equal = equal && caps1.minDstExtent.width == 0; - equal = equal && caps1.minDstExtent.height == 0; - equal = equal && caps1.maxDstExtent.width == 0; - equal = equal && caps1.maxDstExtent.height == 0; - } - return equal; -} - // Test vkGetDisplayPlaneCapabilitiesKHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, GetDispPlaneCapsKHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -4747,7 +4456,7 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneCapsKHRInstanceAndICDSupport) { VkDisplayPlaneCapabilitiesKHR caps{}; ASSERT_EQ(VK_SUCCESS, GetDisplayPlaneCapabilitiesKHR(physical_device, 0, 0, &caps)); - ASSERT_TRUE(CompareDisplayPlaneCaps(caps, env.get_test_icd(0).physical_devices.back().display_plane_capabilities)); + ASSERT_EQ(caps, env.get_test_icd(0).physical_devices.back().display_plane_capabilities); } // Test vkGetDisplayPlaneCapabilitiesKHR where instance supports it with some ICDs that both support @@ -4830,7 +4539,11 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneCapsKHRMixed) { cur_dev.properties.vendorID == pd_props.vendorID) { VkDisplayPlaneCapabilitiesKHR caps{}; ASSERT_EQ(VK_SUCCESS, GetDisplayPlaneCapabilitiesKHR(physical_devices[dev], 0, 0, &caps)); - ASSERT_TRUE(CompareDisplayPlaneCaps(caps, cur_dev.display_plane_capabilities, icd != 1)); + if (icd != 1) { + ASSERT_EQ(caps, cur_dev.display_plane_capabilities); + } else { + ASSERT_EQ(caps, VkDisplayPlaneCapabilitiesKHR{}); + } found = true; break; } @@ -4875,23 +4588,6 @@ TEST(LoaderInstPhysDevExts, PhysDevDispProps2KHRNoICDSupport) { ASSERT_EQ(GetPhysicalDeviceDisplayProperties2KHR, nullptr); } -// Compare the display property data structs -bool CompareDisplayPropData(const std::vector& props1, const std::vector& props2) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - equal = equal && props1[i].display == props2[i].displayProperties.display; - equal = equal && props1[i].physicalDimensions.width == props2[i].displayProperties.physicalDimensions.width; - equal = equal && props1[i].physicalDimensions.height == props2[i].displayProperties.physicalDimensions.height; - equal = equal && props1[i].physicalResolution.width == props2[i].displayProperties.physicalResolution.width; - equal = equal && props1[i].physicalResolution.height == props2[i].displayProperties.physicalResolution.height; - equal = equal && props1[i].supportedTransforms == props2[i].displayProperties.supportedTransforms; - equal = equal && props1[i].planeReorderPossible == props2[i].displayProperties.planeReorderPossible; - equal = equal && props1[i].persistentContent == props2[i].displayProperties.persistentContent; - } - return equal; -} - // Test vGetPhysicalDeviceDisplayProperties2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevDispProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -4932,8 +4628,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispProps2KHRInstanceAndICDSupport) { props2.resize(prop_count_2, {VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR}); ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceDisplayProperties2KHR(physical_device, &prop_count_2, props2.data())); ASSERT_EQ(prop_count, prop_count_2); - - ASSERT_TRUE(CompareDisplayPropData(props, props2)); + ASSERT_EQ(props, props2); } // Test vkGetPhysicalDeviceDisplayProperties2KHR where instance supports it with some ICDs that both support @@ -5020,8 +4715,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispProps2KHRMixed) { props2.resize(prop_count_2, {VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR}); ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceDisplayProperties2KHR(physical_devices[dev], &prop_count_2, props2.data())); ASSERT_EQ(prop_count, prop_count_2); - - ASSERT_TRUE(CompareDisplayPropData(props, props2)); + ASSERT_EQ(props, props2); } } @@ -5054,18 +4748,6 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPlaneProps2KHRNoICDSupport) { ASSERT_EQ(GetPhysicalDeviceDisplayPlaneProperties2KHR, nullptr); } -// Compare the display plane property data structs -bool CompareDisplayPlanePropData(const std::vector& props1, - const std::vector& props2) { - if (props1.size() != props2.size()) return false; - bool equal = true; - for (uint32_t i = 0; i < props1.size(); ++i) { - equal = equal && props1[i].currentDisplay == props2[i].displayPlaneProperties.currentDisplay; - equal = equal && props1[i].currentStackIndex == props2[i].displayPlaneProperties.currentStackIndex; - } - return equal; -} - // Test vGetPhysicalDeviceDisplayPlaneProperties2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, PhysDevDispPlaneProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -5105,8 +4787,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPlaneProps2KHRInstanceAndICDSupport) { ASSERT_EQ(prop_count, prop_count2); props2.resize(prop_count2, {VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR}); ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceDisplayPlaneProperties2KHR(physical_device, &prop_count2, props2.data())); - - ASSERT_TRUE(CompareDisplayPlanePropData(props, props2)); + ASSERT_EQ(props, props2); } // Test vkGetPhysicalDeviceDisplayPlaneProperties2KHR where instance supports it with some ICDs that both support @@ -5192,8 +4873,7 @@ TEST(LoaderInstPhysDevExts, PhysDevDispPlaneProps2KHRMixed) { ASSERT_EQ(prop_count, prop_count2); props2.resize(prop_count2, {VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR}); ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceDisplayPlaneProperties2KHR(physical_devices[dev], &prop_count2, props2.data())); - - ASSERT_TRUE(CompareDisplayPlanePropData(props, props2)); + ASSERT_EQ(props, props2); } } @@ -5224,22 +4904,6 @@ TEST(LoaderInstPhysDevExts, GetDispModeProps2KHRNoICDSupport) { ASSERT_EQ(GetDisplayModeProperties2KHR, nullptr); } -// Compare the display mode properties data structs -bool CompareDisplayModeProps(const std::vector& disps1, - const std::vector& disps2) { - if (disps1.size() != disps2.size()) return false; - - bool equal = true; - for (uint32_t i = 0; i < disps1.size(); ++i) { - equal = equal && disps1[i].displayMode == disps2[i].displayModeProperties.displayMode; - equal = equal && disps1[i].parameters.visibleRegion.width == disps2[i].displayModeProperties.parameters.visibleRegion.width; - equal = - equal && disps1[i].parameters.visibleRegion.height == disps2[i].displayModeProperties.parameters.visibleRegion.height; - equal = equal && disps1[i].parameters.refreshRate == disps2[i].displayModeProperties.parameters.refreshRate; - } - return equal; -} - // Test vGetDisplayModeProperties2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, GetDispModeProps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -5277,8 +4941,7 @@ TEST(LoaderInstPhysDevExts, GetDispModeProps2KHRInstanceAndICDSupport) { ASSERT_EQ(props_count1, props_count2); props2.resize(props_count2, {VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR}); ASSERT_EQ(VK_SUCCESS, GetDisplayModeProperties2KHR(physical_device, VK_NULL_HANDLE, &props_count2, props2.data())); - - ASSERT_TRUE(CompareDisplayModeProps(props, props2)); + ASSERT_EQ(props, props2); } // Test vkGetDisplayModeProperties2KHR where instance supports it with some ICDs that both support @@ -5362,8 +5025,7 @@ TEST(LoaderInstPhysDevExts, GetDispModeProps2KHRMixed) { ASSERT_EQ(props_count1, props_count2); props2.resize(props_count2, {VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR}); ASSERT_EQ(VK_SUCCESS, GetDisplayModeProperties2KHR(physical_devices[dev], VK_NULL_HANDLE, &props_count2, props2.data())); - - ASSERT_TRUE(CompareDisplayModeProps(props, props2)); + ASSERT_EQ(props, props2); } } @@ -5394,29 +5056,6 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneCaps2KHRNoICDSupport) { ASSERT_EQ(GetDisplayPlaneCapabilitiesKHR, nullptr); } -// Compare the display plane caps -bool CompareDisplayPlaneCaps(const VkDisplayPlaneCapabilitiesKHR& caps1, VkDisplayPlaneCapabilities2KHR& caps2) { - bool equal = true; - equal = equal && caps1.supportedAlpha == caps2.capabilities.supportedAlpha; - equal = equal && caps1.minSrcPosition.x == caps2.capabilities.minSrcPosition.x; - equal = equal && caps1.minSrcPosition.y == caps2.capabilities.minSrcPosition.y; - equal = equal && caps1.maxSrcPosition.x == caps2.capabilities.maxSrcPosition.x; - equal = equal && caps1.maxSrcPosition.y == caps2.capabilities.maxSrcPosition.y; - equal = equal && caps1.minSrcExtent.width == caps2.capabilities.minSrcExtent.width; - equal = equal && caps1.minSrcExtent.height == caps2.capabilities.minSrcExtent.height; - equal = equal && caps1.maxSrcExtent.width == caps2.capabilities.maxSrcExtent.width; - equal = equal && caps1.maxSrcExtent.height == caps2.capabilities.maxSrcExtent.height; - equal = equal && caps1.minDstPosition.x == caps2.capabilities.minDstPosition.x; - equal = equal && caps1.minDstPosition.y == caps2.capabilities.minDstPosition.y; - equal = equal && caps1.maxDstPosition.x == caps2.capabilities.maxDstPosition.x; - equal = equal && caps1.maxDstPosition.y == caps2.capabilities.maxDstPosition.y; - equal = equal && caps1.minDstExtent.width == caps2.capabilities.minDstExtent.width; - equal = equal && caps1.minDstExtent.height == caps2.capabilities.minDstExtent.height; - equal = equal && caps1.maxDstExtent.width == caps2.capabilities.maxDstExtent.width; - equal = equal && caps1.maxDstExtent.height == caps2.capabilities.maxDstExtent.height; - return equal; -} - // Test vkGetDisplayPlaneCapabilities2KHR where instance and ICD supports it, but device does not support it. TEST(LoaderInstPhysDevExts, GetDispPlaneCaps2KHRInstanceAndICDSupport) { FrameworkEnvironment env{}; @@ -5446,7 +5085,7 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneCaps2KHRInstanceAndICDSupport) { VkDisplayPlaneCapabilities2KHR caps2{}; VkDisplayPlaneInfo2KHR info{VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR}; ASSERT_EQ(VK_SUCCESS, GetDisplayPlaneCapabilities2KHR(physical_device, &info, &caps2)); - ASSERT_TRUE(CompareDisplayPlaneCaps(caps, caps2)); + ASSERT_EQ(caps, caps2); } // Test vkGetDisplayPlaneCapabilities2KHR where instance supports it with some ICDs that both support @@ -5522,7 +5161,7 @@ TEST(LoaderInstPhysDevExts, GetDispPlaneCaps2KHRMixed) { VkDisplayPlaneCapabilities2KHR caps2{}; VkDisplayPlaneInfo2KHR info{VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR}; ASSERT_EQ(VK_SUCCESS, GetDisplayPlaneCapabilities2KHR(physical_devices[dev], &info, &caps2)); - CompareDisplayPlaneCaps(caps, caps2); + ASSERT_EQ(caps, caps2); } } diff --git a/tests/loader_regression_tests.cpp b/tests/loader_regression_tests.cpp index 407d8093e144a12efe91e7a34864e673c41db28e..437b445f848c2aaa8b43abdb675534eb8f63f446 100644 --- a/tests/loader_regression_tests.cpp +++ b/tests/loader_regression_tests.cpp @@ -394,7 +394,7 @@ TEST(EnumerateDeviceExtensionProperties, ZeroPhysicalDeviceExtensions) { InstWrapper inst{env.vulkan_functions}; inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); auto phys_dev = inst.GetPhysDev(); DeviceWrapper dev{inst}; @@ -810,7 +810,7 @@ TEST(EnumeratePhysicalDevices, ZeroPhysicalDevices) { env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); InstWrapper inst{env.vulkan_functions}; inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); uint32_t count = 0; ASSERT_EQ(VK_ERROR_INITIALIZATION_FAILED, env.vulkan_functions.vkEnumeratePhysicalDevices(inst, &count, nullptr)); @@ -1163,6 +1163,66 @@ TEST(EnumeratePhysicalDevices, MultipleAddRemoves) { ASSERT_GE(found_items[6], 4U); } +TEST(EnumeratePhysicalDevices, OneDriverWithWrongErrorCodes) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + + { + env.get_test_icd().set_enum_physical_devices_return_code(VK_ERROR_INITIALIZATION_FAILED); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_ERROR_INITIALIZATION_FAILED, + env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 0); + } + { + env.get_test_icd().set_enum_physical_devices_return_code(VK_ERROR_INCOMPATIBLE_DRIVER); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_ERROR_INITIALIZATION_FAILED, + env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 0); + } + { + env.get_test_icd().set_enum_physical_devices_return_code(VK_ERROR_SURFACE_LOST_KHR); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_ERROR_INITIALIZATION_FAILED, + env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 0); + } +} + +TEST(EnumeratePhysicalDevices, TwoDriversOneWithWrongErrorCodes) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + TestICD& icd1 = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + + { + icd1.set_enum_physical_devices_return_code(VK_ERROR_INITIALIZATION_FAILED); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 1); + } + { + icd1.set_enum_physical_devices_return_code(VK_ERROR_INCOMPATIBLE_DRIVER); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 1); + } + { + icd1.set_enum_physical_devices_return_code(VK_ERROR_SURFACE_LOST_KHR); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 1); + } +} + TEST(CreateDevice, ExtensionNotPresent) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device("physical_device_0"); @@ -1337,8 +1397,8 @@ TEST(TryLoadWrongBinaries, WrongICD) { #if _WIN32 || _WIN64 ASSERT_TRUE(log.find("Failed to open dynamic library")); #endif -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) -#if defined(__x86_64__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) +#if defined(__x86_64__) || __ppc64__ || __aarch64__ ASSERT_TRUE(log.find("wrong ELF class: ELFCLASS32")); #else ASSERT_TRUE(log.find("wrong ELF class: ELFCLASS64")); @@ -1400,7 +1460,7 @@ TEST(TryLoadWrongBinaries, WrongImplicit) { // We don't want to return VK_ERROR_LAYER_NOT_PRESENT for missing implicit layers because it's not the // application asking for them. - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); #if !defined(__APPLE__) // Should get an info message for the bad implicit layer @@ -1533,7 +1593,7 @@ TEST(TryLoadWrongBinaries, BadImplicit) { // We don't want to return VK_ERROR_LAYER_NOT_PRESENT for missing implicit layers because it's not the // application asking for them. - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); // Should get an info message for the bad implicit ASSERT_TRUE(log.find(std::string("Requested layer \"") + std::string(layer_name) + std::string("\" failed to load."))); @@ -1603,7 +1663,8 @@ TEST(TryLoadWrongBinaries, WrongArchLayer) { FillDebugUtilsCreateDetails(inst.create_info, log); inst.create_info.add_layer(layer_name); inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); - ASSERT_TRUE(log.find("Layer library architecture doesn't match the current running architecture, skipping this layer")); + ASSERT_TRUE(log.find(std::string("The library architecture in layer ") + env.get_shimmed_layer_manifest_path(0).string() + + " doesn't match the current running architecture, skipping this layer")); } TEST(EnumeratePhysicalDeviceGroups, OneCall) { @@ -1898,19 +1959,10 @@ TEST(EnumeratePhysicalDeviceGroups, TwoCallIncomplete) { ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDeviceGroups(inst, &returned_group_count, group_props_2.data())); ASSERT_EQ(2U, returned_group_count); - // Make sure the incomplete group items appear in the complete group - for (uint32_t inc_group = 0; inc_group < 1; ++inc_group) { - bool found = false; - for (uint32_t full_group = 0; full_group < 2; ++full_group) { - if (group_props[inc_group].physicalDeviceCount == group_props_2[full_group].physicalDeviceCount && - group_props[inc_group].physicalDevices[0] == group_props_2[full_group].physicalDevices[0] && - group_props[inc_group].physicalDevices[1] == group_props_2[full_group].physicalDevices[1]) { - found = true; - break; - } - } - ASSERT_EQ(true, found); - } + ASSERT_EQ(group_props[0].physicalDeviceCount, group_props_2[0].physicalDeviceCount); + ASSERT_EQ(group_props[0].physicalDevices[0], group_props_2[0].physicalDevices[0]); + ASSERT_EQ(group_props[0].physicalDevices[1], group_props_2[0].physicalDevices[1]); + for (auto& group : group_props) { VkDeviceGroupDeviceCreateInfo group_info{}; group_info.sType = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO; @@ -1948,19 +2000,10 @@ TEST(EnumeratePhysicalDeviceGroups, TwoCallIncomplete) { ASSERT_EQ(VK_SUCCESS, vkEnumeratePhysicalDeviceGroupsKHR(inst, &returned_group_count, group_props_2.data())); ASSERT_EQ(2U, returned_group_count); - // Make sure the incomplete group items appear in the complete group - for (uint32_t inc_group = 0; inc_group < 1; ++inc_group) { - bool found = false; - for (uint32_t full_group = 0; full_group < 2; ++full_group) { - if (group_props[inc_group].physicalDeviceCount == group_props_2[full_group].physicalDeviceCount && - group_props[inc_group].physicalDevices[0] == group_props_2[full_group].physicalDevices[0] && - group_props[inc_group].physicalDevices[1] == group_props_2[full_group].physicalDevices[1]) { - found = true; - break; - } - } - ASSERT_EQ(true, found); - } + ASSERT_EQ(group_props[0].physicalDeviceCount, group_props_2[0].physicalDeviceCount); + ASSERT_EQ(group_props[0].physicalDevices[0], group_props_2[0].physicalDevices[0]); + ASSERT_EQ(group_props[0].physicalDevices[1], group_props_2[0].physicalDevices[1]); + for (auto& group : group_props) { VkDeviceGroupDeviceCreateInfo group_info{}; group_info.sType = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO; @@ -2744,7 +2787,7 @@ TEST(CreateInstance, InstanceNullExtensionPtr) { ASSERT_EQ(env.vulkan_functions.vkCreateInstance(&info, VK_NULL_HANDLE, &inst), VK_ERROR_EXTENSION_NOT_PRESENT); } -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) // NOTE: Sort order only affects Linux TEST(SortedPhysicalDevices, DevicesSortEnabled10NoAppExt) { FrameworkEnvironment env{}; @@ -3154,52 +3197,31 @@ TEST(SortedPhysicalDevices, DevicesSortedDisabled) { ASSERT_EQ(VK_SUCCESS, instance->vkEnumeratePhysicalDevices(instance, &device_count, physical_devices.data())); ASSERT_EQ(device_count, max_phys_devs); - // Make sure the devices are not in the sorted order. The order is really undefined, but the chances of - // it being exactly the expected sorted is very low. - bool sorted = true; - for (uint32_t dev = 0; dev < device_count; ++dev) { - VkPhysicalDeviceProperties props{}; - instance->vkGetPhysicalDeviceProperties(physical_devices[dev], &props); + // make sure the order is what we started with - but its a bit wonky due to the loader reading physical devices "backwards" + VkPhysicalDeviceProperties props{}; + instance->vkGetPhysicalDeviceProperties(physical_devices[0], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU); + ASSERT_STREQ(props.deviceName, "pd5"); - switch (dev) { - case 0: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd4", props.deviceName)) { - sorted = false; - } - break; - case 1: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd0", props.deviceName)) { - sorted = false; - } - break; - case 2: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd3", props.deviceName)) { - sorted = false; - } - break; - case 3: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU || strcmp("pd1", props.deviceName)) { - sorted = false; - } - break; - case 4: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU || strcmp("pd5", props.deviceName)) { - sorted = false; - } - break; - case 5: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_CPU || strcmp("pd2", props.deviceName)) { - sorted = false; - } - break; - default: - ASSERT_EQ(false, true); - } - if (!sorted) { - break; - } - } - ASSERT_EQ(false, sorted); + instance->vkGetPhysicalDeviceProperties(physical_devices[1], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd3"); + + instance->vkGetPhysicalDeviceProperties(physical_devices[2], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd4"); + + instance->vkGetPhysicalDeviceProperties(physical_devices[3], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_CPU); + ASSERT_STREQ(props.deviceName, "pd2"); + + instance->vkGetPhysicalDeviceProperties(physical_devices[4], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd0"); + + instance->vkGetPhysicalDeviceProperties(physical_devices[5], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU); + ASSERT_STREQ(props.deviceName, "pd1"); // Make sure if we call enumerate again, the information is the same std::array physical_devices_again; @@ -3501,64 +3523,39 @@ TEST(SortedPhysicalDevices, DeviceGroupsSortedDisabled) { ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDeviceGroups(inst, &group_count, physical_device_groups.data())); ASSERT_EQ(group_count, max_phys_dev_groups); - // Make sure the devices are not in the sorted order. The order is really undefined, but the chances of - // it being exactly the expected sorted is very low. - bool sorted = true; - uint32_t cur_dev = 0; - for (uint32_t group = 0; group < max_phys_dev_groups; ++group) { - for (uint32_t dev = 0; dev < physical_device_groups[group].physicalDeviceCount; ++dev) { - VkPhysicalDeviceProperties props{}; - inst->vkGetPhysicalDeviceProperties(physical_device_groups[group].physicalDevices[dev], &props); - switch (cur_dev++) { - case 0: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd4", props.deviceName)) { - sorted = false; - } - break; - case 1: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd6", props.deviceName)) { - sorted = false; - } - break; - case 2: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd5", props.deviceName)) { - sorted = false; - } - break; - case 3: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd2", props.deviceName)) { - sorted = false; - } - break; - case 4: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || strcmp("pd0", props.deviceName)) { - sorted = false; - } - break; - case 5: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU || strcmp("pd1", props.deviceName)) { - sorted = false; - } - break; - case 6: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU || strcmp("pd7", props.deviceName)) { - sorted = false; - } - break; - case 7: - if (props.deviceType != VK_PHYSICAL_DEVICE_TYPE_CPU || strcmp("pd3", props.deviceName)) { - sorted = false; - } - break; - default: - ASSERT_EQ(false, true); - } - } - if (!sorted) { - break; - } - } - ASSERT_EQ(false, sorted); + // make sure the order is what we started with - but its a bit wonky due to the loader reading physical devices "backwards" + VkPhysicalDeviceProperties props{}; + inst->vkGetPhysicalDeviceProperties(physical_devices[0], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU); + ASSERT_STREQ(props.deviceName, "pd7"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[1], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd4"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[2], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd5"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[3], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd6"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[4], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_CPU); + ASSERT_STREQ(props.deviceName, "pd3"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[5], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd0"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[6], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU); + ASSERT_STREQ(props.deviceName, "pd1"); + + inst->vkGetPhysicalDeviceProperties(physical_devices[7], &props); + ASSERT_EQ(props.deviceType, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); + ASSERT_STREQ(props.deviceName, "pd2"); // Make sure if we call enumerate again, the information is the same std::array physical_device_groups_again{}; @@ -3806,7 +3803,7 @@ TEST(PortabilityICDConfiguration, PortabilityAndRegularICDPreInstanceFunctions) } #if defined(_WIN32) -TEST(AppPackageDriverDiscovery, AppPackageTest) { +TEST(AppPackageDiscovery, AppPackageDrivers) { FrameworkEnvironment env; env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2}.set_discovery_type(ManifestDiscoveryType::windows_app_package)) .add_physical_device({}); @@ -3814,6 +3811,46 @@ TEST(AppPackageDriverDiscovery, AppPackageTest) { InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); } +TEST(AppPackageDiscovery, AppPackageLayers) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(ManifestICD{}.set_lib_path(TEST_ICD_PATH_VERSION_2))).add_physical_device({}); + + const char* layer_name = "VK_LAYER_test_package_layer"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME")), + "test_package_layer.json") + .set_discovery_type(ManifestDiscoveryType::windows_app_package)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1U); + ASSERT_EQ(layers.size(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name)); +} + +TEST(AppPackageDiscovery, AppPackageICDAndLayers) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2}.set_discovery_type(ManifestDiscoveryType::windows_app_package)) + .add_physical_device({}); + + const char* layer_name = "VK_LAYER_test_package_layer"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME")), + "test_package_layer.json") + .set_discovery_type(ManifestDiscoveryType::windows_app_package)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1U); + ASSERT_EQ(layers.size(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name)); +} // Make sure that stale layer manifests (path to nonexistant file) which have the same name as real manifests don't cause the real // manifests to be skipped. Stale registry entries happen when a registry is written on layer/driver installation but not cleaned up @@ -3824,7 +3861,7 @@ TEST(DuplicateRegistryEntries, Layers) { auto null_path = env.get_folder(ManifestLocation::null).location() / "test_layer.json"; - env.platform_shim->add_manifest(ManifestCategory::explicit_layer, null_path.str()); + env.platform_shim->add_manifest(ManifestCategory::explicit_layer, null_path); const char* layer_name = "TestLayer"; env.add_explicit_layer( @@ -3842,7 +3879,7 @@ TEST(DuplicateRegistryEntries, Layers) { TEST(DuplicateRegistryEntries, Drivers) { FrameworkEnvironment env{}; auto null_path = env.get_folder(ManifestLocation::null).location() / "test_icd_0.json"; - env.platform_shim->add_manifest(ManifestCategory::icd, null_path.str()); + env.platform_shim->add_manifest(ManifestCategory::icd, null_path); env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA}.set_discovery_type(ManifestDiscoveryType::null_dir)) .add_physical_device("physical_device_0") @@ -3852,7 +3889,7 @@ TEST(DuplicateRegistryEntries, Drivers) { InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(std::string("Skipping adding of json file \"") + null_path.str() + + ASSERT_TRUE(env.debug_log.find(std::string("Skipping adding of json file \"") + null_path.string() + "\" from registry \"HKEY_LOCAL_MACHINE\\" VK_DRIVERS_INFO_REGISTRY_LOC "\" to the list due to duplication")); } @@ -3860,8 +3897,8 @@ TEST(DuplicateRegistryEntries, Drivers) { TEST(LibraryLoading, SystemLocations) { FrameworkEnvironment env{}; - EnvVarWrapper ld_library_path("LD_LIBRARY_PATH", env.get_folder(ManifestLocation::driver).location().str()); - ld_library_path.add_to_list(env.get_folder(ManifestLocation::explicit_layer).location().str()); + EnvVarWrapper ld_library_path("LD_LIBRARY_PATH", env.get_folder(ManifestLocation::driver).location().string()); + ld_library_path.add_to_list(env.get_folder(ManifestLocation::explicit_layer).location()); auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_library_path_type(LibraryPathType::default_search_paths)) .add_physical_device({}); @@ -3900,11 +3937,11 @@ TEST(ManifestDiscovery, ValidSymlinkInXDGEnvVar) { .add_physical_device({}); auto driver_path = env.get_icd_manifest_path(0); std::string symlink_name = "symlink_to_driver.json"; - fs::path symlink_path = env.get_folder(ManifestLocation::driver_env_var).location() / symlink_name; + std::filesystem::path symlink_path = env.get_folder(ManifestLocation::driver_env_var).location() / symlink_name; env.get_folder(ManifestLocation::driver_env_var).add_existing_file(symlink_name); int res = symlink(driver_path.c_str(), symlink_path.c_str()); ASSERT_EQ(res, 0); - EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", symlink_path.str()}; + EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", symlink_path}; InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); @@ -3919,7 +3956,7 @@ TEST(ManifestDiscovery, ValidSymlink) { auto driver_path = env.get_icd_manifest_path(0); std::string symlink_name = "symlink_to_driver.json"; - fs::path symlink_path = env.get_folder(ManifestLocation::driver_env_var).location() / symlink_name; + std::filesystem::path symlink_path = env.get_folder(ManifestLocation::driver_env_var).location() / symlink_name; env.get_folder(ManifestLocation::driver_env_var).add_existing_file(symlink_name); int res = symlink(driver_path.c_str(), symlink_path.c_str()); ASSERT_EQ(res, 0); @@ -3935,13 +3972,13 @@ TEST(ManifestDiscovery, ValidSymlink) { TEST(ManifestDiscovery, InvalidSymlinkXDGEnvVar) { FrameworkEnvironment env{FrameworkSettings{}.set_enable_default_search_paths(false)}; std::string symlink_name = "symlink_to_nothing.json"; - fs::path symlink_path = env.get_folder(ManifestLocation::driver_env_var).location() / symlink_name; - fs::path invalid_driver_path = env.get_folder(ManifestLocation::driver).location() / "nothing_here.json"; + std::filesystem::path symlink_path = env.get_folder(ManifestLocation::driver_env_var).location() / symlink_name; + std::filesystem::path invalid_driver_path = env.get_folder(ManifestLocation::driver).location() / "nothing_here.json"; int res = symlink(invalid_driver_path.c_str(), symlink_path.c_str()); ASSERT_EQ(res, 0); env.get_folder(ManifestLocation::driver_env_var).add_existing_file(symlink_name); - EnvVarWrapper xdg_config_dirs_env_var{symlink_path.str()}; + EnvVarWrapper xdg_config_dirs_env_var{symlink_path}; InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); @@ -3952,8 +3989,8 @@ TEST(ManifestDiscovery, InvalidSymlinkXDGEnvVar) { TEST(ManifestDiscovery, InvalidSymlink) { FrameworkEnvironment env{FrameworkSettings{}.set_enable_default_search_paths(false)}; std::string symlink_name = "symlink_to_nothing.json"; - fs::path symlink_path = env.get_folder(ManifestLocation::driver).location() / symlink_name; - fs::path invalid_driver_path = env.get_folder(ManifestLocation::driver_env_var).location() / "nothing_here.json"; + std::filesystem::path symlink_path = env.get_folder(ManifestLocation::driver).location() / symlink_name; + std::filesystem::path invalid_driver_path = env.get_folder(ManifestLocation::driver_env_var).location() / "nothing_here.json"; int res = symlink(invalid_driver_path.c_str(), symlink_path.c_str()); ASSERT_EQ(res, 0); env.get_folder(ManifestLocation::driver).add_existing_file(symlink_name); @@ -4181,7 +4218,7 @@ TEST(InvalidManifest, ICD) { for (size_t i = 0; i < invalid_jsons.size(); i++) { auto file_name = std::string("invalid_driver_") + std::to_string(i) + ".json"; - fs::path new_path = env.get_folder(ManifestLocation::driver).write_manifest(file_name, invalid_jsons[i]); + std::filesystem::path new_path = env.get_folder(ManifestLocation::driver).write_manifest(file_name, invalid_jsons[i]); env.platform_shim->add_manifest(ManifestCategory::icd, new_path); } @@ -4205,7 +4242,8 @@ TEST(InvalidManifest, Layer) { for (size_t i = 0; i < invalid_jsons.size(); i++) { auto file_name = std::string("invalid_implicit_layer_") + std::to_string(i) + ".json"; - fs::path new_path = env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); + std::filesystem::path new_path = + env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); env.platform_shim->add_manifest(ManifestCategory::implicit_layer, new_path); } @@ -4213,27 +4251,40 @@ TEST(InvalidManifest, Layer) { inst.CheckCreate(); } #if defined(WIN32) -void add_dxgi_adapter(FrameworkEnvironment& env, const char* name, LUID luid, uint32_t vendor_id) { +void add_dxgi_adapter(FrameworkEnvironment& env, std::filesystem::path const& name, LUID luid, uint32_t vendor_id) { auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_discovery_type(ManifestDiscoveryType::null_dir)); driver.set_min_icd_interface_version(5); driver.set_max_icd_interface_version(6); driver.setup_WSI(); driver.set_icd_api_version(VK_API_VERSION_1_1); - driver.physical_devices.emplace_back(name); + driver.physical_devices.emplace_back(name.string()); auto& pd0 = driver.physical_devices.back(); pd0.properties.apiVersion = VK_API_VERSION_1_1; - - DXGI_ADAPTER_DESC1 desc{}; - desc.VendorId = known_driver_list.at(vendor_id).vendor_id; - desc.AdapterLuid = luid; - auto wide_name = conver_str_to_wstr(name); - wcsncpy_s(desc.Description, 128, wide_name.c_str(), wide_name.size()); - driver.set_adapterLUID(desc.AdapterLuid); - env.platform_shim->add_dxgi_adapter(GpuType::discrete, desc); - - env.platform_shim->add_d3dkmt_adapter( - D3DKMT_Adapter{static_cast(env.icds.size()) - 1U, desc.AdapterLuid}.add_driver_manifest_path( - env.get_icd_manifest_path(env.icds.size() - 1))); + driver.set_adapterLUID(luid); + + // luid is unique per DXGI/D3DKMT adapters.Don't add extra DXGI device if one is already created. + // Just add path icd path to matching d3dkmt_adapter. + D3DKMT_Adapter* pAdapter = NULL; + for (uint32_t i = 0; i < env.platform_shim->d3dkmt_adapters.size(); i++) { + if (env.platform_shim->d3dkmt_adapters[i].adapter_luid.HighPart == luid.HighPart && + env.platform_shim->d3dkmt_adapters[i].adapter_luid.LowPart == luid.LowPart) { + pAdapter = &env.platform_shim->d3dkmt_adapters[i]; + break; + } + } + if (pAdapter == NULL) { + DXGI_ADAPTER_DESC1 desc{}; + desc.VendorId = known_driver_list.at(vendor_id).vendor_id; + desc.AdapterLuid = luid; + wcsncpy_s(desc.Description, 128, name.c_str(), name.native().size()); + env.platform_shim->add_dxgi_adapter(GpuType::discrete, desc); + + env.platform_shim->add_d3dkmt_adapter( + D3DKMT_Adapter{static_cast(env.icds.size()) - 1U, desc.AdapterLuid}.add_driver_manifest_path( + env.get_icd_manifest_path(env.icds.size() - 1))); + } else { + pAdapter->add_driver_manifest_path(env.get_icd_manifest_path(env.icds.size() - 1)); + } } TEST(EnumerateAdapterPhysicalDevices, SameAdapterLUID_reordered) { @@ -4241,7 +4292,9 @@ TEST(EnumerateAdapterPhysicalDevices, SameAdapterLUID_reordered) { uint32_t physical_count = 3; - // Physical devices are enumerate in reverse order to the drivers insertion into the test framework + // Physical devices are enumerated: + // a) first in the order of LUIDs showing up in DXGIAdapter list + // b) then in the reverse order to the drivers insertion into the test framework add_dxgi_adapter(env, "physical_device_2", LUID{10, 100}, 2); add_dxgi_adapter(env, "physical_device_1", LUID{20, 200}, 1); add_dxgi_adapter(env, "physical_device_0", LUID{10, 100}, 2); @@ -4337,7 +4390,9 @@ TEST(EnumerateAdapterPhysicalDevices, SameAdapterLUID_same_order) { uint32_t physical_count = 3; - // Physical devices are enumerate in reverse order to the drivers insertion into the test framework + // Physical devices are enumerated: + // a) first in the order of LUIDs showing up in DXGIAdapter list + // b) then in the reverse order to the drivers insertion into the test framework add_dxgi_adapter(env, "physical_device_2", LUID{10, 100}, 2); add_dxgi_adapter(env, "physical_device_1", LUID{20, 200}, 1); add_dxgi_adapter(env, "physical_device_0", LUID{10, 100}, 2); @@ -4385,4 +4440,426 @@ TEST(EnumerateAdapterPhysicalDevices, SameAdapterLUID_same_order) { env.vulkan_functions.vkGetPhysicalDeviceProperties2(physical_device_handles[2], &props2); EXPECT_EQ(layered_driver_properties_msft.underlyingAPI, VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT); } + +TEST(EnumerateAdapterPhysicalDevices, WrongErrorCodes) { + FrameworkEnvironment env; + + add_dxgi_adapter(env, "physical_device_0", LUID{10, 100}, 2); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI().set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + // TestICD only fails in EnumAdapters, so shouldn't fail to query VkPhysicalDevices + { + env.get_test_icd().set_enum_adapter_physical_devices_return_code(VK_ERROR_INITIALIZATION_FAILED); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 1); + } + { + env.get_test_icd().set_enum_adapter_physical_devices_return_code(VK_ERROR_INCOMPATIBLE_DRIVER); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 1); + } + { + env.get_test_icd().set_enum_adapter_physical_devices_return_code(VK_ERROR_SURFACE_LOST_KHR); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 1); + } + + // TestICD fails in EnumPhysDevs, should return VK_ERROR_INCOMPATIBLE_DRIVER + auto check_icds = [&env, &inst] { + env.get_test_icd().set_enum_adapter_physical_devices_return_code(VK_ERROR_INITIALIZATION_FAILED); + uint32_t returned_physical_count = 0; + EXPECT_EQ(VK_ERROR_INITIALIZATION_FAILED, + env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 0); + + env.get_test_icd().set_enum_adapter_physical_devices_return_code(VK_ERROR_INCOMPATIBLE_DRIVER); + returned_physical_count = 0; + EXPECT_EQ(VK_ERROR_INITIALIZATION_FAILED, + env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 0); + + env.get_test_icd().set_enum_adapter_physical_devices_return_code(VK_ERROR_SURFACE_LOST_KHR); + returned_physical_count = 0; + EXPECT_EQ(VK_ERROR_INITIALIZATION_FAILED, + env.vulkan_functions.vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); + EXPECT_EQ(returned_physical_count, 0); + }; + + // TestICD fails in EnumPhysDevs, should return VK_ERROR_INCOMPATIBLE_DRIVER + env.get_test_icd().set_enum_physical_devices_return_code(VK_ERROR_INITIALIZATION_FAILED); + check_icds(); + + // TestICD fails in EnumPhysDevs, should return VK_ERROR_INCOMPATIBLE_DRIVER + env.get_test_icd().set_enum_physical_devices_return_code(VK_ERROR_INCOMPATIBLE_DRIVER); + check_icds(); + + // TestICD fails in EnumPhysDevs, should return VK_ERROR_INCOMPATIBLE_DRIVER + env.get_test_icd().set_enum_physical_devices_return_code(VK_ERROR_SURFACE_LOST_KHR); + check_icds(); +} + +TEST(EnumerateAdapterPhysicalDevices, ManyAdapters) { + FrameworkEnvironment env; + + uint32_t icd_count = 10; + for (uint32_t i = 0; i < icd_count; i++) { + // Add 2 separate physical devices with the same luid + LUID luid{10U + i, static_cast(100U + i)}; + add_dxgi_adapter(env, std::string("physical_device_") + std::to_string(i), luid, 2); + add_dxgi_adapter(env, std::string("physical_device_") + std::to_string(i + icd_count), luid, 2); + } + uint32_t device_count = icd_count * 2; + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI().set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + + auto physical_devices = inst.GetPhysDevs(device_count); + for (auto physical_device : physical_devices) { + DeviceWrapper dev{inst}; + dev.CheckCreate(physical_device); + } +} #endif // defined(WIN32) + +void try_create_swapchain(InstWrapper& inst, VkPhysicalDevice physical_device, DeviceWrapper& dev, VkSurfaceKHR const& surface) { + PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR = inst.load("vkGetPhysicalDeviceSurfaceSupportKHR"); + PFN_vkCreateSwapchainKHR CreateSwapchainKHR = dev.load("vkCreateSwapchainKHR"); + PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR = dev.load("vkGetSwapchainImagesKHR"); + PFN_vkDestroySwapchainKHR DestroySwapchainKHR = dev.load("vkDestroySwapchainKHR"); + ASSERT_TRUE(nullptr != GetPhysicalDeviceSurfaceSupportKHR); + ASSERT_TRUE(nullptr != CreateSwapchainKHR); + ASSERT_TRUE(nullptr != GetSwapchainImagesKHR); + ASSERT_TRUE(nullptr != DestroySwapchainKHR); + + VkBool32 supported = false; + ASSERT_EQ(VK_SUCCESS, GetPhysicalDeviceSurfaceSupportKHR(physical_device, 0, surface, &supported)); + ASSERT_EQ(supported, VK_TRUE); + + VkSwapchainKHR swapchain{}; + VkSwapchainCreateInfoKHR swap_create_info{}; + swap_create_info.surface = surface; + + ASSERT_EQ(VK_SUCCESS, CreateSwapchainKHR(dev, &swap_create_info, nullptr, &swapchain)); + uint32_t count = 0; + ASSERT_EQ(VK_SUCCESS, GetSwapchainImagesKHR(dev, swapchain, &count, nullptr)); + ASSERT_GT(count, 0U); + std::array images; + ASSERT_EQ(VK_SUCCESS, GetSwapchainImagesKHR(dev, swapchain, &count, images.data())); + DestroySwapchainKHR(dev, swapchain, nullptr); +} + +void add_driver_for_unloading_testing(FrameworkEnvironment& env) { + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .add_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) + .setup_WSI() + .add_physical_device(PhysicalDevice{} + .add_extension("VK_KHR_swapchain") + .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, true}) + .finish()); +} + +void add_empty_driver_for_unloading_testing(FrameworkEnvironment& env) { + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME).setup_WSI(); +} + +TEST(DriverUnloadingFromZeroPhysDevs, InterspersedThroughout) { + FrameworkEnvironment env{}; + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + + DebugUtilsLogger debug_log{VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT}; + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI().add_extension("VK_EXT_debug_report"); + FillDebugUtilsCreateDetails(inst.create_info, debug_log); + inst.CheckCreate(); + DebugUtilsWrapper log{inst}; + ASSERT_EQ(VK_SUCCESS, CreateDebugUtilsMessenger(log)); + + PFN_vkSubmitDebugUtilsMessageEXT submit_message = inst.load("vkSubmitDebugUtilsMessageEXT"); + ASSERT_TRUE(submit_message != nullptr); + + VkSurfaceKHR pre_surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, pre_surface)); + WrappedHandle pre_enum_phys_devs_surface{ + pre_surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR}; + + VkDebugReportCallbackEXT debug_callback{}; + VkDebugReportCallbackCreateInfoEXT debug_report_create_info{}; + ASSERT_EQ(VK_SUCCESS, create_debug_callback(inst, debug_report_create_info, debug_callback)); + WrappedHandle + pre_enum_phys_devs_debug_report_callback{debug_callback, inst.inst, env.vulkan_functions.vkDestroyDebugReportCallbackEXT}; + + auto phys_devs = inst.GetPhysDevs(); + std::vector> messengers; + std::vector> surfaces; + for (uint32_t i = 0; i < 35; i++) { + VkDebugUtilsMessengerEXT messenger; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkCreateDebugUtilsMessengerEXT(inst.inst, log.get(), nullptr, &messenger)); + messengers.emplace_back(messenger, inst.inst, env.vulkan_functions.vkDestroyDebugUtilsMessengerEXT); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + surfaces.emplace_back(surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR); + } + for (const auto& phys_dev : phys_devs) { + DeviceWrapper dev{inst}; + dev.create_info.add_extension("VK_KHR_swapchain"); + dev.CheckCreate(phys_dev); + for (const auto& surface : surfaces) { + try_create_swapchain(inst, phys_dev, dev, surface.handle); + } + } +} + +TEST(DriverUnloadingFromZeroPhysDevs, InMiddleOfList) { + FrameworkEnvironment env{}; + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + inst.create_info.setup_WSI().add_extension("VK_EXT_debug_report"); + inst.CheckCreate(); + DebugUtilsWrapper log{inst}; + ASSERT_EQ(VK_SUCCESS, CreateDebugUtilsMessenger(log)); + + VkSurfaceKHR pre_surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, pre_surface)); + WrappedHandle pre_enum_phys_devs_surface{ + pre_surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR}; + + VkDebugReportCallbackEXT debug_callback{}; + VkDebugReportCallbackCreateInfoEXT debug_report_create_info{}; + ASSERT_EQ(VK_SUCCESS, create_debug_callback(inst, debug_report_create_info, debug_callback)); + WrappedHandle + pre_enum_phys_devs_debug_report_callback{debug_callback, inst.inst, env.vulkan_functions.vkDestroyDebugReportCallbackEXT}; + + auto phys_devs = inst.GetPhysDevs(); + std::vector> messengers; + std::vector> surfaces; + for (uint32_t i = 0; i < 35; i++) { + VkDebugUtilsMessengerEXT messenger; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkCreateDebugUtilsMessengerEXT(inst.inst, log.get(), nullptr, &messenger)); + messengers.emplace_back(messenger, inst.inst, env.vulkan_functions.vkDestroyDebugUtilsMessengerEXT); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + surfaces.emplace_back(surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR); + } + for (const auto& phys_dev : phys_devs) { + DeviceWrapper dev{inst}; + dev.create_info.add_extension("VK_KHR_swapchain"); + dev.CheckCreate(phys_dev); + for (const auto& surface : surfaces) { + try_create_swapchain(inst, phys_dev, dev, surface.handle); + } + } +} + +TEST(DriverUnloadingFromZeroPhysDevs, AtFrontAndBack) { + FrameworkEnvironment env{}; + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + + DebugUtilsLogger debug_log{VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT}; + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI().add_extension("VK_EXT_debug_report"); + FillDebugUtilsCreateDetails(inst.create_info, debug_log); + inst.CheckCreate(); + + DebugUtilsWrapper log{inst}; + ASSERT_EQ(VK_SUCCESS, CreateDebugUtilsMessenger(log)); + + VkSurfaceKHR pre_surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, pre_surface)); + WrappedHandle pre_enum_phys_devs_surface{ + pre_surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR}; + + VkDebugReportCallbackEXT debug_callback{}; + VkDebugReportCallbackCreateInfoEXT debug_report_create_info{}; + ASSERT_EQ(VK_SUCCESS, create_debug_callback(inst, debug_report_create_info, debug_callback)); + WrappedHandle + pre_enum_phys_devs_debug_report_callback{debug_callback, inst.inst, env.vulkan_functions.vkDestroyDebugReportCallbackEXT}; + + auto phys_devs = inst.GetPhysDevs(); + std::vector> messengers; + std::vector> surfaces; + for (uint32_t i = 0; i < 35; i++) { + VkDebugUtilsMessengerEXT messenger; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkCreateDebugUtilsMessengerEXT(inst.inst, log.get(), nullptr, &messenger)); + messengers.emplace_back(messenger, inst.inst, env.vulkan_functions.vkDestroyDebugUtilsMessengerEXT); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + surfaces.emplace_back(surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR); + } + for (const auto& phys_dev : phys_devs) { + DeviceWrapper dev{inst}; + dev.create_info.add_extension("VK_KHR_swapchain"); + dev.CheckCreate(phys_dev); + + for (const auto& surface : surfaces) { + try_create_swapchain(inst, phys_dev, dev, surface.handle); + } + } +} + +TEST(DriverUnloadingFromZeroPhysDevs, MultipleEnumerateCalls) { + FrameworkEnvironment env{}; + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + + uint32_t extension_count = 0; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, 0)); + ASSERT_EQ(extension_count, 6U); // default extensions + surface extensions + std::array extensions; + ASSERT_EQ(VK_SUCCESS, + env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data())); + + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto phys_devs1 = inst.GetPhysDevs(); + auto phys_devs2 = inst.GetPhysDevs(); + } + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto phys_devs1 = inst.GetPhysDevs(); + auto phys_devs2 = inst.GetPhysDevs(); + } +} +TEST(DriverUnloadingFromZeroPhysDevs, NoPhysicalDevices) { + FrameworkEnvironment env{}; + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + + DebugUtilsLogger debug_log{VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT}; + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI().add_extension("VK_EXT_debug_report"); + FillDebugUtilsCreateDetails(inst.create_info, debug_log); + inst.CheckCreate(); + DebugUtilsWrapper log{inst}; + ASSERT_EQ(VK_SUCCESS, CreateDebugUtilsMessenger(log)); + + VkSurfaceKHR pre_surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, pre_surface)); + WrappedHandle pre_enum_phys_devs_surface{ + pre_surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR}; + + VkDebugReportCallbackEXT debug_callback{}; + VkDebugReportCallbackCreateInfoEXT debug_report_create_info{}; + ASSERT_EQ(VK_SUCCESS, create_debug_callback(inst, debug_report_create_info, debug_callback)); + WrappedHandle + pre_enum_phys_devs_debug_report_callback{debug_callback, inst.inst, env.vulkan_functions.vkDestroyDebugReportCallbackEXT}; + + // No physical devices == VK_ERROR_INITIALIZATION_FAILED + inst.GetPhysDevs(VK_ERROR_INITIALIZATION_FAILED); + + std::vector> messengers; + std::vector> surfaces; + for (uint32_t i = 0; i < 35; i++) { + VkDebugUtilsMessengerEXT messenger; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkCreateDebugUtilsMessengerEXT(inst.inst, log.get(), nullptr, &messenger)); + messengers.emplace_back(messenger, inst.inst, env.vulkan_functions.vkDestroyDebugUtilsMessengerEXT); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + surfaces.emplace_back(surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR); + } +} + +TEST(DriverUnloadingFromZeroPhysDevs, HandleRecreation) { + FrameworkEnvironment env{}; + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + add_driver_for_unloading_testing(env); + add_empty_driver_for_unloading_testing(env); + + DebugUtilsLogger debug_log{VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT}; + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI().add_extension("VK_EXT_debug_report"); + FillDebugUtilsCreateDetails(inst.create_info, debug_log); + inst.CheckCreate(); + DebugUtilsWrapper log{inst}; + ASSERT_EQ(VK_SUCCESS, CreateDebugUtilsMessenger(log)); + + PFN_vkSubmitDebugUtilsMessageEXT submit_message = inst.load("vkSubmitDebugUtilsMessageEXT"); + ASSERT_TRUE(submit_message != nullptr); + + VkSurfaceKHR pre_surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, pre_surface)); + WrappedHandle pre_enum_phys_devs_surface{ + pre_surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR}; + + VkDebugReportCallbackEXT debug_callback{}; + VkDebugReportCallbackCreateInfoEXT debug_report_create_info{}; + ASSERT_EQ(VK_SUCCESS, create_debug_callback(inst, debug_report_create_info, debug_callback)); + WrappedHandle + pre_enum_phys_devs_debug_report_callback{debug_callback, inst.inst, env.vulkan_functions.vkDestroyDebugReportCallbackEXT}; + + auto phys_devs = inst.GetPhysDevs(); + std::vector> messengers; + std::vector> surfaces; + for (uint32_t i = 0; i < 35; i++) { + VkDebugUtilsMessengerEXT messenger; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkCreateDebugUtilsMessengerEXT(inst.inst, log.get(), nullptr, &messenger)); + messengers.emplace_back(messenger, inst.inst, env.vulkan_functions.vkDestroyDebugUtilsMessengerEXT); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + surfaces.emplace_back(surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR); + } + // Remove some elements arbitrarily - remove 15 of each + // Do it backwards so the indexes are 'corect' + for (uint32_t i = 31; i > 2; i -= 2) { + messengers.erase(messengers.begin() + i); + surfaces.erase(surfaces.begin() + i); + } + // Add in another 100 + for (uint32_t i = 0; i < 100; i++) { + VkDebugUtilsMessengerEXT messenger; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkCreateDebugUtilsMessengerEXT(inst.inst, log.get(), nullptr, &messenger)); + messengers.emplace_back(messenger, inst.inst, env.vulkan_functions.vkDestroyDebugUtilsMessengerEXT); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + surfaces.emplace_back(surface, inst.inst, env.vulkan_functions.vkDestroySurfaceKHR); + } + for (const auto& phys_dev : phys_devs) { + DeviceWrapper dev{inst}; + dev.create_info.add_extension("VK_KHR_swapchain"); + dev.CheckCreate(phys_dev); + for (const auto& surface : surfaces) { + try_create_swapchain(inst, phys_dev, dev, surface.handle); + } + } + VkDebugUtilsMessengerCallbackDataEXT data{}; + data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; + data.pMessage = "I'm a test message!"; + data.messageIdNumber = 1; + submit_message(inst.inst, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, &data); + + ASSERT_EQ(120U + 1U, log.count(data.pMessage)); +} diff --git a/tests/loader_settings_tests.cpp b/tests/loader_settings_tests.cpp index 03c01439eb7ef2e62b94d7621acd6d391e8c3f4a..b3313c7b0e7b1b5e6f282999d552dd03a5cc5d26 100644 --- a/tests/loader_settings_tests.cpp +++ b/tests/loader_settings_tests.cpp @@ -27,11 +27,13 @@ #include "test_environment.h" +#include + std::string get_settings_location_log_message([[maybe_unused]] FrameworkEnvironment const& env, [[maybe_unused]] bool use_secure = false) { std::string s = "Using layer configurations found in loader settings from "; #if defined(WIN32) - return s + env.get_folder(ManifestLocation::settings_location).location().str() + "\\vk_loader_settings.json"; + return s + (env.get_folder(ManifestLocation::settings_location).location() / "vk_loader_settings.json").string(); #elif COMMON_UNIX_PLATFORMS if (use_secure) return s + "/etc/vulkan/loader_settings.d/vk_loader_settings.json"; @@ -39,23 +41,51 @@ std::string get_settings_location_log_message([[maybe_unused]] FrameworkEnvironm return s + "/home/fake_home/.local/share/vulkan/loader_settings.d/vk_loader_settings.json"; #endif } +enum class LayerType { + exp, + imp, + imp_with_enable_env, +}; +const char* add_layer_and_settings(FrameworkEnvironment& env, const char* layer_name, LayerType layer_type, const char* control) { + if (layer_type == LayerType::imp) { + env.add_implicit_layer( + ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("BADGER" + std::to_string(env.layers.size()))), + std::string(layer_name) + std::to_string(env.layers.size()) + ".json"); + } else if (layer_type == LayerType::imp_with_enable_env) { + env.add_implicit_layer( + ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("BADGER" + std::to_string(env.layers.size())) + .set_enable_environment("MUSHROOM" + std::to_string(env.layers.size()))), + std::string(layer_name) + std::to_string(env.layers.size()) + ".json"); + } else if (layer_type == LayerType::exp) { + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + std::string(layer_name) + std::to_string(env.layers.size()) + ".json"}); + } else { + abort(); + } + env.loader_settings.app_specific_settings.back().add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(layer_name) + .set_control(control) + .set_treat_as_implicit_manifest(layer_type != LayerType::exp) + .set_path(env.get_shimmed_layer_manifest_path(env.layers.size() - 1))); + return layer_name; +} // Make sure settings layer is found and that a layer defined in it is loaded TEST(SettingsFile, FileExist) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); - const char* regular_layer_name = "VK_LAYER_TestLayer_0"; - env.add_explicit_layer(TestLayerDetails{ - ManifestLayer{}.add_layer( - ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "regular_test_layer.json"} - .set_discovery_type(ManifestDiscoveryType::override_folder)); - env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_shimmed_layer_manifest_path().str()) - .set_control("on")))); + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all")); + const char* regular_layer_name = add_layer_and_settings(env, "VK_LAYER_TestLayer_0", LayerType::exp, "on"); + env.update_loader_settings(env.loader_settings); { auto layer_props = env.GetLayerProperties(1); EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); @@ -81,11 +111,10 @@ TEST(SettingsFile, SettingsInUnsecuredLocation) { "regular_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_layer_manifest_path().str()) - .set_control("on")))); + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on")))); { auto layer_props = env.GetLayerProperties(1); EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); @@ -123,11 +152,10 @@ TEST(SettingsFile, SettingsInSecuredLocation) { "regular_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_layer_manifest_path().str()) - .set_control("on")))); + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on")))); { auto layer_props = env.GetLayerProperties(1); EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); @@ -158,7 +186,7 @@ TEST(SettingsFile, SettingsInSecuredLocation) { } // Make sure settings file can have multiple sets of settings -TEST(SettingsFile, SupportsMultipleSetingsSimultaneously) { +TEST(SettingsFile, SupportsMultipleSettingsSimultaneously) { FrameworkEnvironment env{}; const char* app_specific_layer_name = "VK_LAYER_TestLayer_0"; env.add_explicit_layer(TestLayerDetails{ @@ -179,7 +207,7 @@ TEST(SettingsFile, SupportsMultipleSetingsSimultaneously) { .add_stderr_log_filter("all") .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(app_specific_layer_name) - .set_path(env.get_layer_manifest_path(0).str()) + .set_path(env.get_layer_manifest_path(0)) .set_control("on")) .add_app_key("key0")) // configuration that should never be used @@ -198,7 +226,7 @@ TEST(SettingsFile, SupportsMultipleSetingsSimultaneously) { .add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( LoaderSettingsLayerConfiguration{} .set_name(global_layer_name) - .set_path(env.get_layer_manifest_path(1).str()) + .set_path(env.get_layer_manifest_path(1)) .set_control("on")))); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); { @@ -215,7 +243,7 @@ TEST(SettingsFile, SupportsMultipleSetingsSimultaneously) { } env.debug_log.clear(); // Set one set to contain the current executable path - env.loader_settings.app_specific_settings.at(0).add_app_key(fs::fixup_backslashes_in_path(test_platform_executable_path())); + env.loader_settings.app_specific_settings.at(0).add_app_key(test_platform_executable_path()); env.update_loader_settings(env.loader_settings); { auto layer_props = env.GetLayerProperties(1); @@ -245,10 +273,7 @@ TEST(SettingsFile, LayerAutoEnabledByEnvVars) { env.update_loader_settings( env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(layer_name) - .set_path(env.get_layer_manifest_path(0).str()) - .set_control("auto")))); + LoaderSettingsLayerConfiguration{}.set_name(layer_name).set_path(env.get_layer_manifest_path(0)).set_control("auto")))); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); { EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", layer_name}; @@ -292,7 +317,7 @@ TEST(SettingsFile, LayerDisablesImplicitLayer) { AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( LoaderSettingsLayerConfiguration{} .set_name(implicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("off") .set_treat_as_implicit_manifest(true)))); { @@ -337,19 +362,19 @@ TEST(SettingsFile, ImplicitLayersDontInterfere) { ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name2)); } // Now setup the settings file to contain a specific order - env.update_loader_settings(LoaderSettings{}.add_app_specific_setting( - AppSpecificSettings{} - .add_stderr_log_filter("all") - .add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(implicit_layer_name1) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) - .set_control("auto") - .set_treat_as_implicit_manifest(true)) - .add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(implicit_layer_name2) - .set_path(env.get_shimmed_layer_manifest_path(1).str()) - .set_control("auto") - .set_treat_as_implicit_manifest(true)))); + env.update_loader_settings( + LoaderSettings{}.add_app_specific_setting(AppSpecificSettings{} + .add_stderr_log_filter("all") + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name1) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name2) + .set_path(env.get_shimmed_layer_manifest_path(1)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); { auto layer_props = env.GetLayerProperties(2); ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1)); @@ -365,19 +390,19 @@ TEST(SettingsFile, ImplicitLayersDontInterfere) { } // Flip the order and store the settings in the env for later use in the test - env.loader_settings = LoaderSettings{}.add_app_specific_setting( - AppSpecificSettings{} - .add_stderr_log_filter("all") - .add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(implicit_layer_name2) - .set_path(env.get_shimmed_layer_manifest_path(1).str()) - .set_control("auto") - .set_treat_as_implicit_manifest(true)) - .add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(implicit_layer_name1) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) - .set_control("auto") - .set_treat_as_implicit_manifest(true))); + env.loader_settings = + LoaderSettings{}.add_app_specific_setting(AppSpecificSettings{} + .add_stderr_log_filter("all") + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name2) + .set_path(env.get_shimmed_layer_manifest_path(1)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name1) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); { @@ -404,7 +429,7 @@ TEST(SettingsFile, ImplicitLayersDontInterfere) { env.loader_settings.app_specific_settings.at(0).layer_configurations.begin() + 1, LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name3) - .set_path(env.get_shimmed_layer_manifest_path(2).str()) + .set_path(env.get_shimmed_layer_manifest_path(2)) .set_control("on")); env.update_loader_settings(env.loader_settings); { @@ -438,12 +463,11 @@ TEST(SettingsFile, ApplicationEnablesIgnored) { AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("off")))); { ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.create_info.add_layer(explicit_layer_name); ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT)); } @@ -592,12 +616,12 @@ TEST(SettingsFile, UnknownLayersInRightPlace) { .add_stderr_log_filter("all") .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name2) - .set_path(env.get_shimmed_layer_manifest_path(2).str()) + .set_path(env.get_shimmed_layer_manifest_path(2)) .set_control("on")) .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")) .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(implicit_layer_name2) - .set_path(env.get_shimmed_layer_manifest_path(3).str()) + .set_path(env.get_shimmed_layer_manifest_path(3)) .set_control("on") .set_treat_as_implicit_manifest(true)))); @@ -641,11 +665,11 @@ TEST(SettingsFile, MultipleLayersWithSameName) { .add_stderr_log_filter("all") .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("on")) .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(1).str()) + .set_path(env.get_shimmed_layer_manifest_path(1)) .set_control("on")))); auto layer_props = env.GetLayerProperties(2); ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name)); @@ -679,11 +703,11 @@ TEST(SettingsFile, MultipleLayersWithSamePath) { .add_stderr_log_filter("all") .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("on")) .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("on")))); auto layer_props = env.GetLayerProperties(1); @@ -721,7 +745,7 @@ TEST(SettingsFile, MismatchedLayerNameAndManifestPath) { AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( LoaderSettingsLayerConfiguration{} .set_name(settings_explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("on")))); ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); @@ -733,6 +757,123 @@ TEST(SettingsFile, MismatchedLayerNameAndManifestPath) { ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } +// Settings file should take precedence over the meta layer, if present +TEST(SettingsFile, ImplicitLayerWithEnableEnvironment) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + + const char* explicit_layer_1 = "VK_LAYER_Regular_TestLayer"; + env.add_explicit_layer( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"); + + const char* implicit_layer_1 = "VK_LAYER_RegularImplicit_TestLayer"; + env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(implicit_layer_1) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("AndISaidHey") + .set_enable_environment("WhatsGoingOn")), + "implicit_layer1.json"); + + const char* explicit_layer_2 = "VK_LAYER_Regular_TestLayer1"; + env.add_explicit_layer(TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer2.json")); + + const char* implicit_layer_2 = "VK_LAYER_RegularImplicit_TestLayer2"; + env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(implicit_layer_2) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("HeyHeyHeyyaya") + .set_enable_environment("HeyHeyHeyhey")), + "implicit_layer2.json")); + const char* explicit_layer_3 = "VK_LAYER_Regular_TestLayer3"; + env.add_explicit_layer(TestLayerDetails( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer3.json")); + env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{} + .add_stderr_log_filter("all") + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_1) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(false)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_1) + .set_path(env.get_shimmed_layer_manifest_path(1)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_2) + .set_path(env.get_shimmed_layer_manifest_path(2)) + .set_control("auto") + .set_treat_as_implicit_manifest(false)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_2) + .set_path(env.get_shimmed_layer_manifest_path(3)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_3) + .set_path(env.get_shimmed_layer_manifest_path(4)) + .set_control("on") + .set_treat_as_implicit_manifest(false)))); + { + auto layer_props = env.GetLayerProperties(5); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_1)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_2)); + ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_2)); + ASSERT_TRUE(string_eq(layer_props.at(4).layerName, explicit_layer_3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_3)); + } + { + EnvVarWrapper enable_meta_layer{"WhatsGoingOn", "1"}; + auto layer_props = env.GetLayerProperties(5); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_1)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_2)); + ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_2)); + ASSERT_TRUE(string_eq(layer_props.at(4).layerName, explicit_layer_3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_3)); + } + { + EnvVarWrapper enable_meta_layer{"HeyHeyHeyhey", "1"}; + auto layer_props = env.GetLayerProperties(5); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_1)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_2)); + ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_2)); + ASSERT_TRUE(string_eq(layer_props.at(4).layerName, explicit_layer_3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_2)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_3)); + } +} + // Settings file should take precedence over the meta layer, if present TEST(SettingsFile, MetaLayerAlsoActivates) { FrameworkEnvironment env{}; @@ -787,13 +928,13 @@ TEST(SettingsFile, MetaLayerAlsoActivates) { .add_stderr_log_filter("all") .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(settings_explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("on") .set_treat_as_implicit_manifest(false)) .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")) .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(settings_implicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(1).str()) + .set_path(env.get_shimmed_layer_manifest_path(1)) .set_control("auto") .set_treat_as_implicit_manifest(true)))); { @@ -876,22 +1017,22 @@ TEST(SettingsFile, LayerOrdering) { std::vector layer_configs{4}; layer_configs.at(0) .set_name(explicit_layer_name1) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_control("on") .set_treat_as_implicit_manifest(false); layer_configs.at(1) .set_name(explicit_layer_name2) - .set_path(env.get_shimmed_layer_manifest_path(1).str()) + .set_path(env.get_shimmed_layer_manifest_path(1)) .set_control("on") .set_treat_as_implicit_manifest(false); layer_configs.at(2) .set_name(implicit_layer_name1) - .set_path(env.get_shimmed_layer_manifest_path(2).str()) + .set_path(env.get_shimmed_layer_manifest_path(2)) .set_control("on") .set_treat_as_implicit_manifest(true); layer_configs.at(3) .set_name(implicit_layer_name2) - .set_path(env.get_shimmed_layer_manifest_path(3).str()) + .set_path(env.get_shimmed_layer_manifest_path(3)) .set_control("on") .set_treat_as_implicit_manifest(true); @@ -972,7 +1113,7 @@ TEST(SettingsFile, EnvVarsWork_VK_LAYER_PATH) { .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(non_env_var_layer_name2) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(2).str())) + .set_path(env.get_shimmed_layer_manifest_path(2))) .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")))); { auto layer_props = env.GetLayerProperties(3); @@ -1053,15 +1194,15 @@ TEST(SettingsFile, EnvVarsWork_VK_ADD_LAYER_PATH) { .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name1) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(1).str())) + .set_path(env.get_shimmed_layer_manifest_path(1))) .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(non_env_var_layer_name2) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(2).str())) + .set_path(env.get_shimmed_layer_manifest_path(2))) .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(implicit_layer_name1) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true)))); { auto layer_props = env.GetLayerProperties(3); @@ -1091,72 +1232,427 @@ TEST(SettingsFile, EnvVarsWork_VK_ADD_LAYER_PATH) { } } -TEST(SettingsFile, EnvVarsWork_VK_INSTANCE_LAYERS) { +TEST(SettingsFile, EnvVarsWork_VK_IMPLICIT_LAYER_PATH) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); - const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1"; + const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1"; env.add_explicit_layer(TestLayerDetails{ ManifestLayer{}.add_layer( - ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "explicit_test_layer1.json"}); + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"} + .set_discovery_type(ManifestDiscoveryType::env_var)); + + const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1"; + env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name1) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Domierigato")), + "implicit_layer1.json"); + const char* settings_layer_path = "VK_LAYER_Regular_TestLayer2"; + env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(settings_layer_path) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Domierigato")), + "implicit_test_layer2.json"}); { - EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name}; - auto layer_props = env.GetLayerProperties(1); - ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name)); + auto layer_props = env.GetLayerProperties(3); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, settings_layer_path)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1)); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); - auto layer = inst.GetActiveLayers(inst.GetPhysDev(), 1); - ASSERT_TRUE(string_eq(layer.at(0).layerName, explicit_layer_name)); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_path)); } - env.update_loader_settings( - env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(explicit_layer_name) - .set_control("off") - .set_path(env.get_shimmed_layer_manifest_path(0).str())))); { - ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.create_info.add_layer(explicit_layer_name1); + inst.CheckCreate(); + ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_path)); + ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1)); + } + env.update_loader_settings(env.loader_settings.add_app_specific_setting( + AppSpecificSettings{} + .add_stderr_log_filter("all") + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(settings_layer_path) + .set_control("on") + .set_path(env.get_shimmed_layer_manifest_path(2)) + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")))); + { + auto layer_props = env.GetLayerProperties(3); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, settings_layer_path)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1)); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_layer_path)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1)); } { - EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name}; - ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.create_info.add_layer(explicit_layer_name1); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_layer_path)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1)); + } +} + +TEST(SettingsFile, EnvVarsWork_VK_ADD_IMPLICIT_LAYER_PATH) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + + const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1"; + env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name1) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("Domierigato")), + "implicit_layer1.json"); + const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"} + .set_discovery_type(ManifestDiscoveryType::add_env_var)); + const char* settings_layer_name = "VK_LAYER_Regular_TestLayer2"; + env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(settings_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("gozaimasu")), + "implicit_test_layer2.json"}); + + { + auto layer_props = env.GetLayerProperties(3); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, settings_layer_name)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name)); + } + { + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.create_info.add_layer(explicit_layer_name1); + inst.CheckCreate(); + ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name)); + ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1)); + } + + env.update_loader_settings(env.loader_settings.add_app_specific_setting( + AppSpecificSettings{} + .add_stderr_log_filter("all") + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name1) + .set_control("on") + .set_path(env.get_shimmed_layer_manifest_path(1))) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(settings_layer_name) + .set_control("on") + .set_path(env.get_shimmed_layer_manifest_path(2)) + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name1) + .set_control("on") + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_treat_as_implicit_manifest(true)))); + { + auto layer_props = env.GetLayerProperties(3); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, settings_layer_name)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, implicit_layer_name1)); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name)); + ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1)); + } + { + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.create_info.add_layer(explicit_layer_name1); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, settings_layer_name)); + ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1)); } } -// Make sure that layers disabled by settings file aren't enabled by VK_LOADER_LAYERS_ENABLE -TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_ENABLE) { + +TEST(SettingsFile, EnvVarsWork_VK_INSTANCE_LAYERS) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + const char* filler_layer_name = "VK_LAYER_filler"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(filler_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "filler_layer.json"}); + const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1"; env.add_explicit_layer(TestLayerDetails{ ManifestLayer{}.add_layer( ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), "explicit_test_layer1.json"}); - env.update_loader_settings( - env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(explicit_layer_name) - .set_control("off") - .set_path(env.get_shimmed_layer_manifest_path(0).str())))); - + { + EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name}; + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); + auto layer = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layer.at(0).layerName, explicit_layer_name)); + } + env.update_loader_settings(env.loader_settings.add_app_specific_setting( + AppSpecificSettings{} + .add_stderr_log_filter("all") + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(filler_layer_name) + .set_control("auto") + .set_path(env.get_shimmed_layer_manifest_path(0))) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name) + .set_control("off") + .set_path(env.get_shimmed_layer_manifest_path(1))))); + { + auto layer_props = env.GetLayerProperties(1); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + } + EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name}; + { + auto layer_props = env.GetLayerProperties(1); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "auto"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "on"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, filler_layer_name)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name)); + } +} + +TEST(SettingsFile, EnvVarsWork_VK_INSTANCE_LAYERS_multiple_layers) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + + const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"}); + + const char* explicit_layer_name2 = "VK_LAYER_Regular_TestLayer2"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer2.json"}); + + const char* explicit_layer_name3 = "VK_LAYER_Regular_TestLayer3"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer3.json"}); + + EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS"}; + vk_instance_layers.add_to_list(explicit_layer_name2); + vk_instance_layers.add_to_list(explicit_layer_name1); + { + auto layer_props = env.GetLayerProperties(3); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name2)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); + auto layer = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layer.at(0).layerName, explicit_layer_name2)); + ASSERT_TRUE(string_eq(layer.at(1).layerName, explicit_layer_name1)); + } + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configurations( + {LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name1) + .set_control("off") + .set_path(env.get_shimmed_layer_manifest_path(0)), + LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name2) + .set_control("off") + .set_path(env.get_shimmed_layer_manifest_path(1)), + LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name3) + .set_control("auto") + .set_path(env.get_shimmed_layer_manifest_path(2))})); + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(1); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "auto"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "on"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "off"; + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "auto"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name2)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "on"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name2)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).control = "auto"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name2)); + } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).control = "on"; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(3); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name2)); + ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name3)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1)); + ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name2)); + } +} + +// Make sure that layers disabled by settings file aren't enabled by VK_LOADER_LAYERS_ENABLE +TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_ENABLE) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all")); + const char* explicit_layer_name = add_layer_and_settings(env, "VK_LAYER_Regular_TestLayer1", LayerType::exp, "off"); + env.update_loader_settings(env.loader_settings); + EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name}; ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); @@ -1166,6 +1662,67 @@ TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_ENABLE) { ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } + +TEST(SettingsFile, settings_disable_layer_enabled_by_env_vars) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all")); + std::vector layer_names; + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_alpha", LayerType::imp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_bravo", LayerType::imp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_charlie", LayerType::imp, "auto")); + env.loader_settings.app_specific_settings.back().add_layer_configuration( + LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_delta", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_echo", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_foxtrot", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_gamma", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_indigo", LayerType::exp, "auto")); + auto disable_layer_name = add_layer_and_settings(env, "VK_LAYER_juniper", LayerType::exp, "off"); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_kangaroo", LayerType::exp, "on")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_lima", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_mango", LayerType::exp, "auto")); + + env.update_loader_settings(env.loader_settings); + { + EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", disable_layer_name}; + auto layer_props = env.GetLayerProperties(11); + for (size_t i = 0; i < layer_props.size(); i++) { + ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName)); + } + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4); + ASSERT_TRUE(string_eq(layer_names.at(0), layers.at(0).layerName)); + ASSERT_TRUE(string_eq(layer_names.at(1), layers.at(1).layerName)); + ASSERT_TRUE(string_eq(layer_names.at(2), layers.at(2).layerName)); + ASSERT_TRUE(string_eq(layer_names.at(8), layers.at(3).layerName)); + } + { + EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_ENABLE", disable_layer_name}; + auto layer_props = env.GetLayerProperties(11); + for (size_t i = 0; i < layer_props.size(); i++) { + ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName)); + } + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4); + ASSERT_TRUE(string_eq(layer_names.at(0), layers.at(0).layerName)); + ASSERT_TRUE(string_eq(layer_names.at(1), layers.at(1).layerName)); + ASSERT_TRUE(string_eq(layer_names.at(2), layers.at(2).layerName)); + ASSERT_TRUE(string_eq(layer_names.at(8), layers.at(3).layerName)); + } + { + auto layer_props = env.GetLayerProperties(11); + for (size_t i = 0; i < layer_props.size(); i++) { + ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName)); + } + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(disable_layer_name); + inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); + } +} + // Make sure that layers enabled by settings file aren't disabled by VK_LOADER_LAYERS_ENABLE TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_DISABLE) { FrameworkEnvironment env{}; @@ -1182,7 +1739,7 @@ TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_DISABLE) { LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str())))); + .set_path(env.get_shimmed_layer_manifest_path(0))))); EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_DISABLE", explicit_layer_name}; auto layer_props = env.GetLayerProperties(1); @@ -1192,7 +1749,8 @@ TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_DISABLE) { FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name)); } #if defined(WIN32) @@ -1209,11 +1767,10 @@ TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) { "regular_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_layer_manifest_path().str()) - .set_control("on")))); + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on")))); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); auto layer_props = env.GetLayerProperties(1); @@ -1224,7 +1781,6 @@ TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) { inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - env.debug_log.clear(); auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); } @@ -1242,11 +1798,10 @@ TEST(SettingsFile, MultipleKeysInRegistryInSecureLocation) { "regular_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_layer_manifest_path().str()) - .set_control("on")))); + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on")))); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); // Make sure it works if the settings file is in the HKEY_LOCAL_MACHINE @@ -1261,7 +1816,6 @@ TEST(SettingsFile, MultipleKeysInRegistryInSecureLocation) { inst.CheckCreate(); ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - env.debug_log.clear(); auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); } @@ -1296,7 +1850,7 @@ TEST(SettingsFile, PreInstanceFunctions) { LoaderSettingsLayerConfiguration{} .set_name(implicit_layer_name) .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0).str()) + .set_path(env.get_shimmed_layer_manifest_path(0)) .set_treat_as_implicit_manifest(true))); env.update_loader_settings(env.loader_settings); { @@ -1388,12 +1942,39 @@ TEST(SettingsFile, PreInstanceFunctions) { // If an implicit layer's disable environment variable is set, but the settings file says to turn the layer on, the layer should be // activated. TEST(SettingsFile, ImplicitLayerDisableEnvironmentVariableOverriden) { - auto check_log_for_insert_instance_layer_string = [](FrameworkEnvironment& env, const char* implicit_layer_name, - bool check_for_enable) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); + + const char* filler_layer_name = "VK_LAYER_filler"; + env.add_explicit_layer( + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(filler_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer.json"); + + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME") + .set_enable_environment("ENABLE_ME")), + "implicit_test_layer.json"); + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configurations( + {LoaderSettingsLayerConfiguration{} + .set_name(filler_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(false), + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(1)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)})); + + auto check_log_for_insert_instance_layer_string = [&env, implicit_layer_name](bool check_for_enable) { { InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(VK_SUCCESS); + inst.CheckCreate(); if (check_for_enable) { ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name)); auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); @@ -1406,230 +1987,779 @@ TEST(SettingsFile, ImplicitLayerDisableEnvironmentVariableOverriden) { env.debug_log.clear(); }; - FrameworkEnvironment env; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; - - env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name(implicit_layer_name) - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) - .set_disable_environment("DISABLE_ME") - .set_enable_environment("ENABLE_ME")), - "implicit_test_layer.json"); - env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(implicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path(0).str()) - .set_treat_as_implicit_manifest(true))); - // control is set to on - env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("on"); + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).set_control("on"); env.update_loader_settings(env.loader_settings); { EnvVarWrapper enable_env_var{"ENABLE_ME"}; EnvVarWrapper disable_env_var{"DISABLE_ME"}; - auto layers = env.GetLayerProperties(1); - ASSERT_TRUE(string_eq(layers[0].layerName, implicit_layer_name)); + auto layers = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layers[0].layerName, filler_layer_name)); + ASSERT_TRUE(string_eq(layers[1].layerName, implicit_layer_name)); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); enable_env_var.set_new_value("0"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); enable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); enable_env_var.remove_value(); disable_env_var.set_new_value("0"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); disable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); enable_env_var.set_new_value("1"); disable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); } // control is set to off - env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("off"); + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).set_control("off"); env.update_loader_settings(env.loader_settings); { EnvVarWrapper enable_env_var{"ENABLE_ME"}; EnvVarWrapper disable_env_var{"DISABLE_ME"}; - ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + auto layers = env.GetLayerProperties(1); + ASSERT_TRUE(string_eq(layers[0].layerName, filler_layer_name)); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.set_new_value("0"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.remove_value(); disable_env_var.set_new_value("0"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); disable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.set_new_value("1"); disable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); } // control is set to auto - env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("auto"); + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(1).set_control("auto"); env.update_loader_settings(env.loader_settings); { EnvVarWrapper enable_env_var{"ENABLE_ME"}; EnvVarWrapper disable_env_var{"DISABLE_ME"}; - auto layers = env.GetLayerProperties(1); - ASSERT_TRUE(string_eq(layers[0].layerName, implicit_layer_name)); + auto layers = env.GetLayerProperties(2); + ASSERT_TRUE(string_eq(layers[0].layerName, filler_layer_name)); + ASSERT_TRUE(string_eq(layers[1].layerName, implicit_layer_name)); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.set_new_value("0"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, true); + check_log_for_insert_instance_layer_string(true); enable_env_var.remove_value(); disable_env_var.set_new_value("0"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); disable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); enable_env_var.set_new_value("1"); disable_env_var.set_new_value("1"); - check_log_for_insert_instance_layer_string(env, implicit_layer_name, false); + check_log_for_insert_instance_layer_string(false); } } -// Settings can say which filters to use - make sure those are propagated & treated correctly -TEST(SettingsFile, StderrLogFilters) { +TEST(SettingsFile, ImplicitLayersNotAccidentallyEnabled) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); - const char* explicit_layer_name = "Regular_TestLayer1"; - env.add_explicit_layer(TestLayerDetails{ - ManifestLayer{}.add_layer( - ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), - "explicit_test_layer1.json"}); - env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{} - .add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(explicit_layer_name) - .set_path(env.get_shimmed_layer_manifest_path().str()) - .set_control("on")) - .add_layer_configuration( - LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("on")))); + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all")); + std::vector layer_names; - std::string expected_output_verbose; - expected_output_verbose += "Layer Configurations count = 2\n"; - expected_output_verbose += "---- Layer Configuration [0] ----\n"; - expected_output_verbose += std::string("Name: ") + explicit_layer_name + "\n"; - expected_output_verbose += "Path: " + env.get_shimmed_layer_manifest_path().str() + "\n"; - expected_output_verbose += "Control: on\n"; - expected_output_verbose += "---- Layer Configuration [1] ----\n"; - expected_output_verbose += "Name: VK_LAYER_missing\n"; - expected_output_verbose += "Path: /road/to/nowhere\n"; - expected_output_verbose += "Control: on\n"; - expected_output_verbose += "---------------------------------\n"; - - std::string expected_output_info = get_settings_location_log_message(env) + "\n"; + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_alpha", LayerType::imp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_bravo", LayerType::imp_with_enable_env, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_charlie", LayerType::imp_with_enable_env, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_delta", LayerType::imp_with_enable_env, "auto")); - std::string expected_output_warning = - "Layer name Regular_TestLayer1 does not conform to naming standard (Policy #LLP_LAYER_3)\n"; + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_echo", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_foxtrot", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_gamma", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_indigo", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_juniper", LayerType::exp, "auto")); - std::string expected_output_error = "loader_get_json: Failed to open JSON file /road/to/nowhere\n"; + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_kangaroo", LayerType::exp, "on")); + + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_lima", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_mango", LayerType::exp, "auto")); + layer_names.push_back(add_layer_and_settings(env, "VK_LAYER_niagara", LayerType::exp, "auto")); - env.loader_settings.app_specific_settings.at(0).stderr_log = {"all"}; + env.update_loader_settings(env.loader_settings); + { + auto layer_props = env.GetLayerProperties(13); + for (size_t i = 0; i < layer_props.size(); i++) { + ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName)); + } + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_names.at(0))); + ASSERT_TRUE(string_eq(layers.at(1).layerName, layer_names.at(9))); + } + + { + EnvVarWrapper env_var{"MUSHROOM1", "1"}; + auto layer_props = env.GetLayerProperties(13); + for (size_t i = 0; i < layer_props.size(); i++) { + ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName)); + } + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_names.at(0))); + ASSERT_TRUE(string_eq(layers.at(1).layerName, layer_names.at(1))); + ASSERT_TRUE(string_eq(layers.at(2).layerName, layer_names.at(9))); + } + { + EnvVarWrapper env_var{"BADGER0", "1"}; + auto layer_props = env.GetLayerProperties(13); + for (size_t i = 0; i < layer_props.size(); i++) { + ASSERT_TRUE(string_eq(layer_names.at(i), layer_props.at(i).layerName)); + } + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_names.at(9))); + } +} + +TEST(SettingsFile, ImplicitLayersPreInstanceEnumInstLayerProps) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME") + .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{} + .set_vk_func("vkEnumerateInstanceLayerProperties") + .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))), + "implicit_test_layer.json"); + + env.update_loader_settings( + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); + + uint32_t layer_props = 43; + auto& layer = env.get_test_layer(0); + layer.set_reported_layer_props(layer_props); + + uint32_t count = 0; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr)); + ASSERT_EQ(count, layer_props); +} + +TEST(SettingsFile, EnableEnvironmentImplicitLayersPreInstanceEnumInstLayerProps) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME") + .set_enable_environment("ENABLE_ME") + .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{} + .set_vk_func("vkEnumerateInstanceLayerProperties") + .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))), + "implicit_test_layer.json"); + + env.update_loader_settings( + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); + + uint32_t layer_props = 43; + auto& layer = env.get_test_layer(0); + layer.set_reported_layer_props(layer_props); + + uint32_t count = 0; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr)); + ASSERT_EQ(count, 1U); + std::array layers{}; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, layers.data())); + ASSERT_EQ(count, 1U); + ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name)); +} + +TEST(SettingsFile, ImplicitLayersPreInstanceEnumInstExtProps) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME") + .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{} + .set_vk_func("vkEnumerateInstanceExtensionProperties") + .set_override_name("test_preinst_vkEnumerateInstanceExtensionProperties"))), + "implicit_test_layer.json"); + + env.update_loader_settings( + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); + + uint32_t ext_props = 52; + auto& layer = env.get_test_layer(0); + layer.set_reported_extension_props(ext_props); + + uint32_t count = 0; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr)); + ASSERT_EQ(count, ext_props); +} + +TEST(SettingsFile, EnableEnvironmentImplicitLayersPreInstanceEnumInstExtProps) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + + env.add_implicit_layer( + ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME") + .set_enable_environment("ENABLE_ME") + .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{} + .set_vk_func("vkEnumerateInstanceExtensionProperties") + .set_override_name("test_preinst_vkEnumerateInstanceExtensionProperties"))), + "implicit_test_layer.json"); + + env.update_loader_settings( + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); + + uint32_t ext_props = 52; + auto& layer = env.get_test_layer(0); + layer.set_reported_extension_props(ext_props); + + auto extensions = env.GetInstanceExtensions(4); + EXPECT_TRUE(string_eq(extensions.at(0).extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)); + EXPECT_TRUE(string_eq(extensions.at(1).extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)); + EXPECT_TRUE(string_eq(extensions.at(2).extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)); + EXPECT_TRUE(string_eq(extensions.at(3).extensionName, VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME)); +} + +TEST(SettingsFile, ImplicitLayersPreInstanceVersion) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)) + .add_physical_device({}) + .set_icd_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3)); + + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + + env.add_implicit_layer(ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3)) + .set_disable_environment("DISABLE_ME") + .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{} + .set_vk_func("vkEnumerateInstanceVersion") + .set_override_name("test_preinst_vkEnumerateInstanceVersion"))), + "implicit_test_layer.json"); + + env.update_loader_settings( + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); + + uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4); + auto& layer = env.get_test_layer(0); + layer.set_reported_instance_version(layer_version); + + uint32_t version = 0; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version)); + ASSERT_EQ(version, layer_version); +} + +TEST(SettingsFile, EnableEnvironmentImplicitLayersPreInstanceVersion) { + FrameworkEnvironment env; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)) + .add_physical_device({}) + .set_icd_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3)); + + const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer"; + + env.add_implicit_layer(ManifestLayer{}.set_file_format_version({1, 1, 2}).add_layer( + ManifestLayer::LayerDescription{} + .set_name(implicit_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3)) + .set_disable_environment("DISABLE_ME") + .set_enable_environment("ENABLE_ME") + .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{} + .set_vk_func("vkEnumerateInstanceVersion") + .set_override_name("test_preinst_vkEnumerateInstanceVersion"))), + "implicit_test_layer.json"); + + env.update_loader_settings( + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(implicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)))); + + uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4); + auto& layer = env.get_test_layer(0); + layer.set_reported_instance_version(layer_version); + + uint32_t version = 0; + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version)); + ASSERT_EQ(version, VK_HEADER_VERSION_COMPLETE); +} + +// Settings can say which filters to use - make sure those are propagated & treated correctly +TEST(SettingsFile, StderrLogFilters) { + FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("")}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + const char* explicit_layer_name = "Regular_TestLayer1"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"}); + env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{} + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path()) + .set_control("on")) + .add_layer_configuration( + LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("on")))); + + std::string expected_output_verbose; + expected_output_verbose += "[Vulkan Loader] DEBUG: Layer Configurations count = 2\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: ---- Layer Configuration [0] ----\n"; + expected_output_verbose += std::string("[Vulkan Loader] DEBUG: Name: ") + explicit_layer_name + "\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Path: " + env.get_shimmed_layer_manifest_path().string() + "\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Layer Type: Explicit\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Control: on\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: ---- Layer Configuration [1] ----\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Name: VK_LAYER_missing\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Path: /road/to/nowhere\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Layer Type: Explicit\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Control: on\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: ---------------------------------\n"; + + std::string expected_output_info = + std::string("[Vulkan Loader] INFO: ") + get_settings_location_log_message(env) + "\n"; + + std::string expected_output_warning = "[Vulkan Loader] WARNING: Layer name " + std::string(explicit_layer_name) + + " does not conform to naming standard (Policy #LLP_LAYER_3)\n"; + + std::string expected_output_error = + "[Vulkan Loader] ERROR: loader_get_json: Failed to open JSON file /road/to/nowhere\n"; + + env.loader_settings.app_specific_settings.at(0).stderr_log = {"all"}; env.update_loader_settings(env.loader_settings); { InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(expected_output_verbose)); - ASSERT_TRUE(env.debug_log.find(expected_output_info)); - ASSERT_TRUE(env.debug_log.find(expected_output_warning)); - ASSERT_TRUE(env.debug_log.find(expected_output_error)); + ASSERT_TRUE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error: ERROR | " + "WARNING | INFO | DEBUG | PERF | DRIVER | LAYER\n")); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_error)); auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); } - env.debug_log.clear(); - env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - env.loader_settings.app_specific_settings.at(0).stderr_log = {"info"}; + env.platform_shim->clear_logs(); + env.loader_settings.app_specific_settings.at(0).stderr_log = {"error", "warn", "info", "debug"}; env.update_loader_settings(env.loader_settings); { InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(expected_output_verbose)); - ASSERT_FALSE(env.debug_log.find(expected_output_info)); - ASSERT_FALSE(env.debug_log.find(expected_output_warning)); - ASSERT_FALSE(env.debug_log.find(expected_output_error)); + ASSERT_TRUE( + env.platform_shim->find_in_log("[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard " + "Error: ERROR | WARNING | INFO | DEBUG\n")); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_error)); auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); } - env.debug_log.clear(); - env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + env.platform_shim->clear_logs(); + env.loader_settings.app_specific_settings.at(0).stderr_log = {"warn", "info", "debug"}; + env.update_loader_settings(env.loader_settings); + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + ASSERT_TRUE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error: WARNING | INFO | DEBUG\n")); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } + env.platform_shim->clear_logs(); env.loader_settings.app_specific_settings.at(0).stderr_log = {"debug"}; env.update_loader_settings(env.loader_settings); { InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_FALSE(env.debug_log.find(expected_output_verbose)); - ASSERT_TRUE(env.debug_log.find(expected_output_info)); - ASSERT_FALSE(env.debug_log.find(expected_output_warning)); - ASSERT_FALSE(env.debug_log.find(expected_output_error)); + ASSERT_TRUE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error: DEBUG\n")); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error)); auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); } - env.debug_log.clear(); - env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + env.platform_shim->clear_logs(); + env.loader_settings.app_specific_settings.at(0).stderr_log = {"info"}; + env.update_loader_settings(env.loader_settings); + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + ASSERT_FALSE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error: INFO\n")); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } + env.platform_shim->clear_logs(); env.loader_settings.app_specific_settings.at(0).stderr_log = {"warn"}; env.update_loader_settings(env.loader_settings); { InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_FALSE(env.debug_log.find(expected_output_verbose)); - ASSERT_FALSE(env.debug_log.find(expected_output_info)); - ASSERT_TRUE(env.debug_log.find(expected_output_warning)); - ASSERT_FALSE(env.debug_log.find(expected_output_error)); + ASSERT_FALSE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error: WARNING\n")); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error)); auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); } - env.debug_log.clear(); - env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + env.platform_shim->clear_logs(); env.loader_settings.app_specific_settings.at(0).stderr_log = {"error"}; env.update_loader_settings(env.loader_settings); { InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_FALSE(env.debug_log.find(expected_output_verbose)); - ASSERT_FALSE(env.debug_log.find(expected_output_info)); - ASSERT_FALSE(env.debug_log.find(expected_output_warning)); - ASSERT_TRUE(env.debug_log.find(expected_output_error)); + ASSERT_FALSE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error: ERROR\n")); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_TRUE(env.platform_shim->find_in_log(expected_output_error)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } + env.platform_shim->clear_logs(); + env.loader_settings.app_specific_settings.at(0).stderr_log = {""}; // Empty string shouldn't be misinterpreted + env.update_loader_settings(env.loader_settings); + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + ASSERT_FALSE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error:")); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } + env.platform_shim->clear_logs(); + env.loader_settings.app_specific_settings.at(0).stderr_log = {}; // No string in the log + env.update_loader_settings(env.loader_settings); + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + ASSERT_FALSE(env.platform_shim->find_in_log( + "[Vulkan Loader] DEBUG: Loader Settings Filters for Logging to Standard Error:")); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_verbose)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_info)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_warning)); + ASSERT_FALSE(env.platform_shim->find_in_log(expected_output_error)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } +} + +// Settings can say which filters to use - make sure the lack of this filter works correctly with VK_LOADER_DEBUG +TEST(SettingsFile, StderrLog_NoOutput) { + FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("")}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + const char* explicit_layer_name = "Regular_TestLayer1"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"}); + env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{} + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path()) + .set_control("auto")) + .add_layer_configuration( + LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto")))); + env.loader_settings.app_specific_settings.at(0).stderr_log = {""}; + env.update_loader_settings(env.loader_settings); + + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty()); + + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 0); + EXPECT_TRUE(active_layer_props.size() == 0); + } + // Check if an empty string (vs lack of the stderr_log field) produces correct output + env.loader_settings.app_specific_settings.at(0).stderr_log = {}; + env.update_loader_settings(env.loader_settings); + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty()); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 0); + EXPECT_TRUE(active_layer_props.size() == 0); + } + + env.loader_settings.app_specific_settings.at(0).stderr_log.clear(); + env.update_loader_settings(env.loader_settings); + { + EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name}; + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty()); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); } + + { + EnvVarWrapper instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name}; + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty()); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } + env.loader_settings.app_specific_settings.at(0).stderr_log = {}; + env.update_loader_settings(env.loader_settings); + { + EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name}; + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty()); + + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } + + { + EnvVarWrapper instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name}; + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + EXPECT_TRUE(env.platform_shim->fputs_stderr_log.empty()); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name)); + } +} + +// Settings can say which filters to use - make sure the lack of this filter works correctly with VK_LOADER_DEBUG +TEST(SettingsFile, NoStderr_log_but_VK_LOADER_DEBUG) { + FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("all")}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + + const char* explicit_layer_name = "Regular_TestLayer1"; + + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_test_layer1.json"}); + + env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{} + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(explicit_layer_name) + .set_path(env.get_shimmed_layer_manifest_path()) + .set_control("auto")) + .add_layer_configuration( + LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto")))); + env.loader_settings.app_specific_settings.at(0).stderr_log = {}; + env.update_loader_settings(env.loader_settings); + + std::string expected_output_verbose; + expected_output_verbose += "[Vulkan Loader] DEBUG: Layer Configurations count = 2\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: ---- Layer Configuration [0] ----\n"; + expected_output_verbose += std::string("[Vulkan Loader] DEBUG: Name: ") + explicit_layer_name + "\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Path: " + env.get_shimmed_layer_manifest_path().string() + "\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Layer Type: Explicit\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Control: auto\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: ---- Layer Configuration [1] ----\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Name: VK_LAYER_missing\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Path: /road/to/nowhere\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Layer Type: Explicit\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: Control: auto\n"; + expected_output_verbose += "[Vulkan Loader] DEBUG: ---------------------------------\n"; + + std::string expected_output_info = + std::string("[Vulkan Loader] INFO: ") + get_settings_location_log_message(env) + "\n"; + + std::string expected_output_warning = + "[Vulkan Loader] WARNING: Layer name Regular_TestLayer1 does not conform to naming standard (Policy #LLP_LAYER_3)\n"; + + std::string expected_output_error = + "[Vulkan Loader] ERROR: loader_get_json: Failed to open JSON file /road/to/nowhere\n"; + + env.platform_shim->clear_logs(); + { + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_verbose)); + EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_info)); + EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_warning)); + EXPECT_TRUE(env.platform_shim->find_in_log(expected_output_error)); + + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 0); + EXPECT_TRUE(active_layer_props.size() == 0); + } +} +TEST(SettingsFile, ManyLayersEnabledInManyWays) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + const char* layer1 = "VK_LAYER_layer1"; + env.add_explicit_layer( + TestLayerDetails{ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_layer1.json"}); + const char* layer2 = "VK_LAYER_layer2"; + env.add_explicit_layer( + TestLayerDetails{ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_layer2.json"}); + const char* layer3 = "VK_LAYER_layer3"; + env.add_explicit_layer( + TestLayerDetails{ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_layer3.json"}); + const char* layer4 = "VK_LAYER_layer4"; + env.add_explicit_layer( + TestLayerDetails{ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer4).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_layer4.json"}); + const char* layer5 = "VK_LAYER_layer5"; + env.add_explicit_layer( + TestLayerDetails{ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer5).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "explicit_layer5.json"}); + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configurations( + {LoaderSettingsLayerConfiguration{}.set_name(layer1).set_path(env.get_shimmed_layer_manifest_path(0)).set_control("on"), + LoaderSettingsLayerConfiguration{} + .set_name(layer2) + .set_path(env.get_shimmed_layer_manifest_path(1)) + .set_control("auto"), + LoaderSettingsLayerConfiguration{} + .set_name(layer3) + .set_path(env.get_shimmed_layer_manifest_path(2)) + .set_control("auto"), + LoaderSettingsLayerConfiguration{} + .set_name(layer4) + .set_path(env.get_shimmed_layer_manifest_path(3)) + .set_control("auto"), + LoaderSettingsLayerConfiguration{} + .set_name(layer5) + .set_path(env.get_shimmed_layer_manifest_path(4)) + .set_control("on")})); + env.update_loader_settings(env.loader_settings); + + EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", layer2}; + EnvVarWrapper vk_loader_layers_enable{"VK_LOADER_LAYERS_ENABLE", layer1}; + + auto layers = env.GetLayerProperties(5); + ASSERT_TRUE(string_eq(layers[0].layerName, layer1)); + ASSERT_TRUE(string_eq(layers[1].layerName, layer2)); + ASSERT_TRUE(string_eq(layers[2].layerName, layer3)); + ASSERT_TRUE(string_eq(layers[3].layerName, layer4)); + ASSERT_TRUE(string_eq(layers[4].layerName, layer5)); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 3); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, layer1)); + EXPECT_TRUE(string_eq(active_layer_props.at(1).layerName, layer2)); + EXPECT_TRUE(string_eq(active_layer_props.at(2).layerName, layer5)); } // Enough layers exist that arrays need to be resized - make sure that works @@ -1648,7 +2778,7 @@ TEST(SettingsFile, TooManyLayers) { .set_discovery_type(ManifestDiscoveryType::override_folder)); env.loader_settings.app_specific_settings.at(0).add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(layer_name + std::to_string(i)) - .set_path(env.get_layer_manifest_path(i).str()) + .set_path(env.get_layer_manifest_path(i)) .set_control("on")); } env.update_loader_settings(env.loader_settings); @@ -1678,7 +2808,7 @@ TEST(SettingsFile, TooManyLayers) { env.loader_settings.app_specific_settings.at(0).add_layer_configuration( LoaderSettingsLayerConfiguration{} .set_name(layer_name + std::to_string(layer_count - i - 1)) - .set_path(env.get_layer_manifest_path(layer_count - i - 1).str()) + .set_path(env.get_layer_manifest_path(layer_count - i - 1)) .set_control("on")); } env.update_loader_settings(env.loader_settings); @@ -1702,3 +2832,189 @@ TEST(SettingsFile, TooManyLayers) { } } } + +TEST(SettingsFile, EnvVarsWorkTogether) { + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device(PhysicalDevice{}.set_deviceName("regular").finish()); + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::env_var)) + .add_physical_device(PhysicalDevice{}.set_deviceName("env_var").finish()); + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::add_env_var)) + .add_physical_device(PhysicalDevice{}.set_deviceName("add_env_var").finish()); + + const char* regular_explicit_layer = "VK_LAYER_regular_explicit_layer"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_explicit_layer).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_explicit_layer.json"}); + const char* regular_explicit_layer_settings_file_set_on = "VK_LAYER_regular_explicit_layer_settings_file_set_on"; + env.add_explicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_explicit_layer_settings_file_set_on) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_explicit_layer_settings_file_set_on.json"}); + + const char* env_var_explicit_layer = "VK_LAYER_env_var_explicit_layer"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(env_var_explicit_layer).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "env_var_explicit_layer.json"} + .set_discovery_type(ManifestDiscoveryType::env_var)); + + const char* add_env_var_explicit_layer = "VK_LAYER_add_env_var_explicit_layer"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(add_env_var_explicit_layer).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "add_env_var_explicit_layer.json"} + .set_discovery_type(ManifestDiscoveryType::add_env_var)); + + const char* regular_implicit_layer = "VK_LAYER_regular_implicit_layer"; + env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_disable_environment("A") + .set_name(regular_implicit_layer) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_implicit_layer.json"}); + + const char* env_var_implicit_layer = "VK_LAYER_env_var_implicit_layer"; + env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_disable_environment("B") + .set_name(env_var_implicit_layer) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "env_var_implicit_layer.json"} + .set_discovery_type(ManifestDiscoveryType::env_var)); + + const char* add_env_var_implicit_layer = "VK_LAYER_add_env_var_implicit_layer"; + env.add_implicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_disable_environment("C") + .set_name(add_env_var_implicit_layer) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "add_env_var_implicit_layer.json"} + .set_discovery_type(ManifestDiscoveryType::add_env_var)); + + // Settings file only contains the one layer it wants enabled + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{} + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_implicit_layer) + .set_path(env.get_shimmed_layer_manifest_path(4)) + .set_control("auto") + .set_treat_as_implicit_manifest(true)) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_explicit_layer) + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_control("auto")) + .add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_explicit_layer_settings_file_set_on) + .set_path(env.get_shimmed_layer_manifest_path(1)) + .set_control("on")) + .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))); + env.update_loader_settings(env.loader_settings); + + { // VK_INSTANCE_LAYERS + EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", regular_explicit_layer}; + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer)); + EXPECT_TRUE(string_eq(layers.at(2).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE(string_eq(layers.at(3).layerName, env_var_implicit_layer)); + EXPECT_TRUE(env.platform_shim->find_in_log( + "env var 'VK_INSTANCE_LAYERS' defined and adding layers: VK_LAYER_regular_explicit_layer")); + } + env.platform_shim->clear_logs(); + { // VK_LOADER_LAYERS_ENABLE + EnvVarWrapper env_var_enable{"VK_LOADER_LAYERS_ENABLE", regular_explicit_layer}; + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer)); + EXPECT_TRUE(string_eq(layers.at(2).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE(string_eq(layers.at(3).layerName, env_var_implicit_layer)); + EXPECT_TRUE(env.platform_shim->find_in_log( + "Layer \"VK_LAYER_regular_explicit_layer\" forced enabled due to env var 'VK_LOADER_LAYERS_ENABLE'")); + } + env.platform_shim->clear_logs(); + { // VK_LOADER_LAYERS_DISABLE + EnvVarWrapper env_var_disable{"VK_LOADER_LAYERS_DISABLE", "~all~"}; // ignored by settings file + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE( + env.platform_shim->find_in_log("Layer \"VK_LAYER_env_var_implicit_layer\" forced disabled because name matches filter " + "of env var 'VK_LOADER_LAYERS_DISABLE'")); + } + env.platform_shim->clear_logs(); + + { // VK_LOADER_LAYERS_ALLOW + EnvVarWrapper env_var_allow{"VK_LOADER_LAYERS_ALLOW", regular_implicit_layer}; + // Allow only makes sense when the disable env-var is also set + EnvVarWrapper env_var_disable{"VK_LOADER_LAYERS_DISABLE", "~implicit~"}; + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2); + // The regular_implicit_layer is set to "auto" so is affected by environment variables + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on)); + + EXPECT_TRUE( + env.platform_shim->find_in_log("Layer \"VK_LAYER_env_var_implicit_layer\" forced disabled because name matches filter " + "of env var 'VK_LOADER_LAYERS_DISABLE'")); + } + env.platform_shim->clear_logs(); + + { // VK_LAYER_PATH + // VK_LAYER_PATH is set by add_explicit_layer() + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(env_var_explicit_layer); + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE(string_eq(layers.at(2).layerName, env_var_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(3).layerName, env_var_explicit_layer)); + EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_env_var_explicit_layer\"")); + } + env.platform_shim->clear_logs(); + { // VK_IMPLICIT_LAYER_PATH + // VK_IMPLICIT_LAYER_PATH is set by add_implicit_layer() + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE(string_eq(layers.at(2).layerName, env_var_implicit_layer)); + EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_env_var_implicit_layer\"")); + } + env.platform_shim->clear_logs(); + { // VK_ADD_LAYER_PATH + // VK_ADD_LAYER_PATH is set by add_explicit_layer(), but we need to disable VK_LAYER_PATH + // since VK_LAYER_PATH overrides VK_ADD_LAYER_PATH + EnvVarWrapper env_var_vk_layer_path{"VK_LAYER_PATH", ""}; + env_var_vk_layer_path.remove_value(); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(add_env_var_explicit_layer); + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE(string_eq(layers.at(2).layerName, env_var_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(3).layerName, add_env_var_explicit_layer)); + EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_add_env_var_explicit_layer\"")); + } + env.platform_shim->clear_logs(); + { // VK_ADD_IMPLICIT_LAYER_PATH + // VK_ADD_IMPLICIT_LAYER_PATH is set by add_explicit_layer(), but we need to disable VK_LAYER_PATH + // since VK_IMPLICIT_LAYER_PATH overrides VK_ADD_IMPLICIT_LAYER_PATH + EnvVarWrapper env_var_vk_layer_path{"VK_IMPLICIT_LAYER_PATH", ""}; + env_var_vk_layer_path.remove_value(); + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3); + EXPECT_TRUE(string_eq(layers.at(0).layerName, regular_implicit_layer)); + EXPECT_TRUE(string_eq(layers.at(1).layerName, regular_explicit_layer_settings_file_set_on)); + EXPECT_TRUE(string_eq(layers.at(2).layerName, add_env_var_implicit_layer)); + EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_add_env_var_implicit_layer\"")); + } +} diff --git a/tests/loader_testing_main.cpp b/tests/loader_testing_main.cpp index 75e145d63fbd929e59c0e1d616189ee4e69ea61c..20af4f54c317321724ed366f67ccc242335019fa 100644 --- a/tests/loader_testing_main.cpp +++ b/tests/loader_testing_main.cpp @@ -56,16 +56,16 @@ int main(int argc, char** argv) { #endif // clean up folders from old test - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "null_dir"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "icd_manifests"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "icd_env_vars_manifests"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_layer_manifests"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_env_var_layer_folder"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_add_env_var_layer_folder"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "implicit_layer_manifests"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "override_layer_manifests"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "app_package_manifests"); - fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "macos_bundle"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "null_dir"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "icd_manifests"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "icd_env_vars_manifests"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_layer_manifests"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_env_var_layer_folder"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_add_env_var_layer_folder"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "implicit_layer_manifests"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "override_layer_manifests"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "app_package_manifests"); + fs::delete_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY) / "macos_bundle"); // make sure the tests don't find these env-vars if they were set on the system EnvVarWrapper vk_icd_filenames_env_var{"VK_ICD_FILENAMES"}; @@ -73,6 +73,8 @@ int main(int argc, char** argv) { EnvVarWrapper vk_add_driver_files_env_var{"VK_ADD_DRIVER_FILES"}; EnvVarWrapper vk_layer_path_env_var{"VK_LAYER_PATH"}; EnvVarWrapper vk_add_layer_path_env_var{"VK_ADD_LAYER_PATH"}; + EnvVarWrapper vk_implicit_layer_path_env_var{"VK_IMPLICIT_LAYER_PATH"}; + EnvVarWrapper vk_add_implicit_layer_path_env_var{"VK_ADD_IMPLICIT_LAYER_PATH"}; EnvVarWrapper vk_instance_layers_env_var{"VK_INSTANCE_LAYERS"}; EnvVarWrapper vk_loader_drivers_select_env_var{"VK_LOADER_DRIVERS_SELECT"}; EnvVarWrapper vk_loader_drivers_disable_env_var{"VK_LOADER_DRIVERS_DISABLE"}; diff --git a/tests/loader_threading_tests.cpp b/tests/loader_threading_tests.cpp index 2a2aadabb40d145836a2b5f284ab487b7db0e6e5..482bd5fb6f26659ecee57b301887e732de27ae2f 100644 --- a/tests/loader_threading_tests.cpp +++ b/tests/loader_threading_tests.cpp @@ -27,9 +27,7 @@ #include "test_environment.h" -#include #include -#include void create_destroy_instance_loop_with_function_queries(FrameworkEnvironment* env, uint32_t num_loops_create_destroy_instance, uint32_t num_loops_try_get_instance_proc_addr, diff --git a/tests/loader_unknown_ext_tests.cpp b/tests/loader_unknown_ext_tests.cpp index dd918b8fa90992351bddcc0d7a5f7ae31ed55310..7822756821670a6c6763f8635eadf7459b7babb1 100644 --- a/tests/loader_unknown_ext_tests.cpp +++ b/tests/loader_unknown_ext_tests.cpp @@ -26,8 +26,6 @@ */ #include "test_environment.h" -#include -#include enum class TestConfig { add_layer_implementation, @@ -710,7 +708,7 @@ TEST(UnknownFunction, DeviceFromGIPAWithLayerImplementation) { } TEST(UnknownFunction, DeviceFromGIPAWithLayerInterception) { - unknown_function_test_impl({TestConfig::add_layer_implementation}); + unknown_function_test_impl({TestConfig::add_layer_interception}); } TEST(UnknownFunction, DeviceFromGIPAWithLayerInterceptionAndLayerImplementation) { @@ -741,7 +739,7 @@ TEST(UnknownFunction, CommandBufferFromGIPAWithLayerImplementation) { } TEST(UnknownFunction, CommandBufferFromGIPAWithLayerInterception) { - unknown_function_test_impl({TestConfig::add_layer_implementation}); + unknown_function_test_impl({TestConfig::add_layer_interception}); } TEST(UnknownFunction, CommandBufferFromGIPAWithLayerInterceptionAndLayerImplementation) { @@ -772,7 +770,7 @@ TEST(UnknownFunction, QueueFromGIPAWithLayer) { } TEST(UnknownFunction, QueueFromGIPAWithLayerInterception) { - unknown_function_test_impl({TestConfig::add_layer_implementation}); + unknown_function_test_impl({TestConfig::add_layer_interception}); } TEST(UnknownFunction, QueueFromGIPAWithLayerInterceptionAndLayerImplementation) { diff --git a/tests/loader_version_tests.cpp b/tests/loader_version_tests.cpp index 8307995acb2085299e8b7e387eb34adfbf8f3011..c13c7f60a04f6cefd60fca62228618261ee8944f 100644 --- a/tests/loader_version_tests.cpp +++ b/tests/loader_version_tests.cpp @@ -306,7 +306,7 @@ TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, VerifyGroupResults #endif // defined(WIN32) TEST(ICDInterfaceVersion7, SingleDriver) { FrameworkEnvironment env{}; - auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7_WITH_ADDITIONAL_EXPORTS)).add_physical_device({}); + auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7)).add_physical_device({}); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); DeviceWrapper dev{inst}; @@ -316,7 +316,7 @@ TEST(ICDInterfaceVersion7, SingleDriver) { TEST(ICDInterfaceVersion7, SingleDriverWithoutExportedFunctions) { FrameworkEnvironment env{}; - auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7)).add_physical_device({}); + auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7_WIHTOUT_EXPORTS)).add_physical_device({}); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); DeviceWrapper dev{inst}; @@ -1380,7 +1380,8 @@ TEST(DriverManifest, VersionMismatchWithEnumerateInstanceVersion) { FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(std::string("terminator_CreateInstance: Manifest ICD for \"") + env.get_test_icd_path().str() + + ASSERT_TRUE(env.debug_log.find(std::string("terminator_CreateInstance: Manifest ICD for \"") + + env.get_test_icd_path().string() + "\" contained a 1.1 or greater API version, but " "vkEnumerateInstanceVersion returned 1.0, treating as a 1.0 ICD")); } @@ -1397,7 +1398,8 @@ TEST(DriverManifest, EnumerateInstanceVersionNotSupported) { FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(std::string("terminator_CreateInstance: Manifest ICD for \"") + env.get_test_icd_path().str() + + ASSERT_TRUE(env.debug_log.find(std::string("terminator_CreateInstance: Manifest ICD for \"") + + env.get_test_icd_path().string() + "\" contained a 1.1 or greater API version, but does " "not support vkEnumerateInstanceVersion, treating as a 1.0 ICD")); } diff --git a/tests/loader_wsi_tests.cpp b/tests/loader_wsi_tests.cpp index 99e8bcbc5c06e998654bebd199448d11c0826b9d..82a27435269039d460e4f95096a2cd6090751f59 100644 --- a/tests/loader_wsi_tests.cpp +++ b/tests/loader_wsi_tests.cpp @@ -758,6 +758,68 @@ TEST(WsiTests, WaylandGetPhysicalDeviceSurfaceSupportKHR) { } #endif +TEST(WsiTests, GoogleSurfaceslessQuery) { + std::vector present_modes{VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR}; + VkSurfaceFormatKHR surface_format = {VK_FORMAT_R8G8B8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR}; + FrameworkEnvironment env{}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .setup_WSI() + .add_instance_extension("VK_GOOGLE_surfaceless_query") + .add_instance_extension("VK_KHR_get_surface_capabilities2") + .add_physical_device(PhysicalDevice{} + .add_extension("VK_KHR_swapchain") +#if defined(WIN32) + .add_extension("VK_EXT_full_screen_exclusive") +#endif + .add_surface_format(surface_format) + .add_surface_present_modes(present_modes) + .finish()); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_extension("VK_KHR_surface"); + inst.create_info.add_extension("VK_GOOGLE_surfaceless_query"); + ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); + + VkPhysicalDevice physical_device = inst.GetPhysDev(); + + uint32_t present_mode_count = 4; + std::vector queried_present_modes{present_mode_count}; + inst->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, VK_NULL_HANDLE, &present_mode_count, + queried_present_modes.data()); + ASSERT_EQ(present_modes, queried_present_modes); + + uint32_t surface_format_count = 1; + std::vector queried_surface_formats{surface_format_count}; + inst->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, VK_NULL_HANDLE, &surface_format_count, + queried_surface_formats.data()); + ASSERT_EQ(std::vector{surface_format}, queried_surface_formats); + + uint32_t surface_format2_count = 1; + std::vector queried_surface_formats2{surface_format2_count}; + VkPhysicalDeviceSurfaceInfo2KHR surface_info{}; + surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; + surface_info.surface = VK_NULL_HANDLE; + inst->vkGetPhysicalDeviceSurfaceFormats2KHR(physical_device, &surface_info, &surface_format_count, + queried_surface_formats2.data()); + ASSERT_EQ(std::vector{surface_format}, queried_surface_formats2); + + VkSurfaceCapabilities2KHR surface_caps2{}; + surface_caps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; + + ASSERT_EQ(VK_SUCCESS, + env.vulkan_functions.vkGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device, &surface_info, &surface_caps2)); + +#if defined(WIN32) + PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT pfn_vkGetPhysicalDeviceSurfacePresentModes2EXT = + inst.load("vkGetPhysicalDeviceSurfacePresentModes2EXT"); + ASSERT_EQ(VK_SUCCESS, pfn_vkGetPhysicalDeviceSurfacePresentModes2EXT(physical_device, &surface_info, &present_mode_count, + queried_present_modes.data())); + ASSERT_EQ(present_modes, queried_present_modes); + +#endif +} + TEST(WsiTests, ForgetEnableSurfaceExtensions) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) @@ -850,3 +912,127 @@ TEST(WsiTests, SwapchainFunctional) { } env.vulkan_functions.vkDestroySurfaceKHR(inst.inst, surface, nullptr); } + +TEST(WsiTests, EXTSurfaceMaintenance1) { + FrameworkEnvironment env{}; + + std::vector present_modes{VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR}; + VkSurfaceCapabilitiesKHR surface_caps{}; + surface_caps.maxImageExtent = VkExtent2D{300, 300}; + surface_caps.minImageExtent = VkExtent2D{100, 100}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .setup_WSI() + .add_instance_extension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME) + .add_physical_device(PhysicalDevice{} + .add_extension("VK_KHR_swapchain") + .set_deviceName("no") + .set_surface_capabilities(surface_caps) + .add_surface_present_modes(present_modes) + .finish()); + VkSurfacePresentScalingCapabilitiesEXT scaling_capabilities{}; + scaling_capabilities.supportedPresentScaling = VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT; + scaling_capabilities.supportedPresentGravityX = VK_PRESENT_SCALING_ASPECT_RATIO_STRETCH_BIT_EXT; + scaling_capabilities.supportedPresentGravityY = VK_PRESENT_SCALING_STRETCH_BIT_EXT; + scaling_capabilities.minScaledImageExtent = {60, 60}; + scaling_capabilities.maxScaledImageExtent = {1000, 1000}; + auto& icd2 = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .setup_WSI() + .add_instance_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME) + .add_instance_extension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME) + .add_physical_device(PhysicalDevice{} + .add_extension("VK_KHR_swapchain") + .set_deviceName("yes") + .set_surface_capabilities(surface_caps) + .add_surface_present_modes(present_modes) + .set_surface_present_scaling_capabilities(scaling_capabilities) + .finish()); + std::vector> compatible_present_modes{ + {VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR}, + {VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR}, + {VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR}, + {VK_PRESENT_MODE_FIFO_RELAXED_KHR, VK_PRESENT_MODE_FIFO_KHR}, + }; + icd2.physical_devices[0].surface_present_mode_compatibility = compatible_present_modes; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .setup_WSI() + .add_physical_device(PhysicalDevice{} + .add_extension("VK_KHR_swapchain") + .set_deviceName("no") + .set_surface_capabilities(surface_caps) + .add_surface_present_modes(present_modes) + .finish()); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.setup_WSI(); + inst.create_info.add_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME); + inst.create_info.add_extension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); + inst.CheckCreate(); + + VkSurfaceKHR surface{}; + ASSERT_EQ(VK_SUCCESS, create_surface(inst, surface)); + WrappedHandle wrapped_surface{surface, inst.inst, + env.vulkan_functions.vkDestroySurfaceKHR}; + auto physical_devices = inst.GetPhysDevs(3); + + for (auto physical_device : physical_devices) { + VkPhysicalDeviceProperties phys_dev_props{}; + inst->vkGetPhysicalDeviceProperties(physical_device, &phys_dev_props); + bool driver_support_surface_maintenance1 = string_eq(phys_dev_props.deviceName, "yes"); + + uint32_t present_mode_count = 4; + std::vector queried_present_modes{present_mode_count}; + inst->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, + queried_present_modes.data()); + + for (uint32_t i = 0; i < present_mode_count; i++) { + VkSurfacePresentModeEXT present_mode{}; + present_mode.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT; + present_mode.presentMode = queried_present_modes[i]; + + VkPhysicalDeviceSurfaceInfo2KHR surface_info{}; + surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; + surface_info.surface = surface; + surface_info.pNext = &present_mode; + + VkSurfacePresentModeCompatibilityEXT SurfacePresentModeCompatibilityEXT{}; + SurfacePresentModeCompatibilityEXT.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT; + + VkSurfacePresentScalingCapabilitiesEXT SurfacePresentScalingCapabilitiesEXT{}; + SurfacePresentScalingCapabilitiesEXT.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT; + SurfacePresentScalingCapabilitiesEXT.pNext = &SurfacePresentModeCompatibilityEXT; + + VkSurfaceCapabilities2KHR surface_caps2{}; + surface_caps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; + surface_caps2.pNext = &SurfacePresentScalingCapabilitiesEXT; + + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device, &surface_info, + &surface_caps2)); + if (driver_support_surface_maintenance1) { + ASSERT_EQ(SurfacePresentModeCompatibilityEXT.presentModeCount, 2U); + } else { + ASSERT_EQ(SurfacePresentModeCompatibilityEXT.presentModeCount, 1U); + } + std::vector queried_compatible_present_modes{SurfacePresentModeCompatibilityEXT.presentModeCount}; + SurfacePresentModeCompatibilityEXT.pPresentModes = queried_compatible_present_modes.data(); + + ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device, &surface_info, + &surface_caps2)); + + if (driver_support_surface_maintenance1) { + ASSERT_EQ(compatible_present_modes[i], queried_compatible_present_modes); + ASSERT_EQ(scaling_capabilities, SurfacePresentScalingCapabilitiesEXT); + } else { + // Make sure the emulation returned the values we expect - 1 compatible present mode which is the mode we are + // querying, Scaling capabilities is 0 (aka none) and the extent is just the surface caps extent + ASSERT_EQ(SurfacePresentModeCompatibilityEXT.presentModeCount, 1U); + ASSERT_EQ(SurfacePresentModeCompatibilityEXT.pPresentModes[0], queried_present_modes[i]); + ASSERT_EQ(SurfacePresentScalingCapabilitiesEXT.supportedPresentScaling, 0U); + ASSERT_EQ(SurfacePresentScalingCapabilitiesEXT.supportedPresentGravityX, 0u); + ASSERT_EQ(SurfacePresentScalingCapabilitiesEXT.supportedPresentGravityY, 0U); + ASSERT_EQ(SurfacePresentScalingCapabilitiesEXT.minScaledImageExtent, surface_caps.minImageExtent); + ASSERT_EQ(SurfacePresentScalingCapabilitiesEXT.maxScaledImageExtent, surface_caps.maxImageExtent); + } + } + } +}