diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..96d755a26185ae6f91e20502134ecb97eb137914 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,306 @@ +#[[ + Build options: + * BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON) + * HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on) + * HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on) + * HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on) + * HTTPLIB_REQUIRE_OPENSSL (default off) + * HTTPLIB_REQUIRE_ZLIB (default off) + * HTTPLIB_REQUIRE_BROTLI (default off) + * HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on) + * HTTPLIB_COMPILE (default off) + * HTTPLIB_INSTALL (default on) + * HTTPLIB_TEST (default off) + * BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed). + * OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed). + + ------------------------------------------------------------------------------- + + After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL ZLIB Brotli) is available. + This creates a httplib::httplib target (if found and if listed components are supported). + It can be linked like so: + + target_link_libraries(your_exe httplib::httplib) + + The following will build & install for later use. + + Linux/macOS: + + mkdir -p build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + sudo cmake --build . --target install + + Windows: + + mkdir build + cd build + cmake .. + runas /user:Administrator "cmake --build . --config Release --target install" + + ------------------------------------------------------------------------------- + + These variables are available after you run find_package(httplib) + * HTTPLIB_HEADER_PATH - this is the full path to the installed header (e.g. /usr/include/httplib.h). + * HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled. + * HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled. + * HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled. + * HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain is enabled. + * HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only. + * HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include). + * HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so). + * httplib_VERSION or HTTPLIB_VERSION - the project's version string. + * HTTPLIB_FOUND - a bool for if the target was found. + + Want to use precompiled headers (Cmake feature since v3.16)? + It's as simple as doing the following (before linking): + + target_precompile_headers(httplib::httplib INTERFACE "${HTTPLIB_HEADER_PATH}") + + ------------------------------------------------------------------------------- + + ARCH_INDEPENDENT option of write_basic_package_version_file() requires Cmake v3.14 +]] +cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR) + +# Get the CPPHTTPLIB_VERSION value and use it as a version +# This gets the string with the CPPHTTPLIB_VERSION value from the header. +# This is so the maintainer doesn't actually need to update this manually. +file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"") + +# Extracts just the version string itself from the whole string contained in _raw_version_string +# since _raw_version_string would contain the entire line of code where it found the version string +string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}") + +project(httplib + VERSION ${_httplib_version} + LANGUAGES CXX + DESCRIPTION "A C++ header-only HTTP/HTTPS server and client library." + HOMEPAGE_URL "https://github.com/yhirose/cpp-httplib" +) + +# Change as needed to set an OpenSSL minimum version. +# This is used in the installed Cmake config file. +set(_HTTPLIB_OPENSSL_MIN_VER "3.0.0") + +# Lets you disable C++ exception during CMake configure time. +# The value is used in the install CMake config file. +option(HTTPLIB_NO_EXCEPTIONS "Disable the use of C++ exceptions" OFF) +# Allow for a build to require OpenSSL to pass, instead of just being optional +option(HTTPLIB_REQUIRE_OPENSSL "Requires OpenSSL to be found & linked, or fails build." OFF) +option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF) +# Allow for a build to casually enable OpenSSL/ZLIB support, but silently continue if not found. +# Make these options so their automatic use can be specifically disabled (as needed) +option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON) +option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON) +# Lets you compile the program as a regular library instead of header-only +option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF) +# Lets you disable the installation (useful when fetched from another CMake project) +option(HTTPLIB_INSTALL "Enables the installation target" ON) +option(HTTPLIB_TEST "Enables testing and builds tests" OFF) +option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF) +option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON) +option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system certs from the Apple Keychain." ON) +# Defaults to static library +option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF) +if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE) + # Necessary for Windows if building shared libs + # See https://stackoverflow.com/a/40743080 + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +# Set some variables that are used in-tree and while building based on our options +set(HTTPLIB_IS_COMPILED ${HTTPLIB_COMPILE}) +set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN ${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}) + +# Threads needed for on some systems, and for on Linux +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads REQUIRED) +# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS. +if(HTTPLIB_REQUIRE_OPENSSL) + find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL REQUIRED) + set(HTTPLIB_IS_USING_OPENSSL TRUE) +elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE) + find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL QUIET) + # Avoid a rare circumstance of not finding all components but the end-user did their + # own call for OpenSSL, which might trick us into thinking we'd otherwise have what we wanted + if (TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto) + set(HTTPLIB_IS_USING_OPENSSL ${OPENSSL_FOUND}) + else() + set(HTTPLIB_IS_USING_OPENSSL FALSE) + endif() +endif() + +if(HTTPLIB_REQUIRE_ZLIB) + find_package(ZLIB REQUIRED) + set(HTTPLIB_IS_USING_ZLIB TRUE) +elseif(HTTPLIB_USE_ZLIB_IF_AVAILABLE) + find_package(ZLIB QUIET) + # FindZLIB doesn't have a ZLIB_FOUND variable, so check the target. + if(TARGET ZLIB::ZLIB) + set(HTTPLIB_IS_USING_ZLIB TRUE) + endif() +endif() + +# Adds our cmake folder to the search path for find_package +# This is so we can use our custom FindBrotli.cmake +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +if(HTTPLIB_REQUIRE_BROTLI) + find_package(Brotli COMPONENTS encoder decoder common REQUIRED) + set(HTTPLIB_IS_USING_BROTLI TRUE) +elseif(HTTPLIB_USE_BROTLI_IF_AVAILABLE) + find_package(Brotli COMPONENTS encoder decoder common QUIET) + set(HTTPLIB_IS_USING_BROTLI ${Brotli_FOUND}) +endif() + +# Used for default, common dirs that the end-user can change (if needed) +# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR +include(GNUInstallDirs) + +if(HTTPLIB_COMPILE) + # Put the split script into the build dir + configure_file(split.py "${CMAKE_CURRENT_BINARY_DIR}/split.py" + COPYONLY + ) + # Needs to be in the same dir as the python script + configure_file(httplib.h "${CMAKE_CURRENT_BINARY_DIR}/httplib.h" + COPYONLY + ) + + # Used outside of this if-else + set(_INTERFACE_OR_PUBLIC PUBLIC) + # Brings in the Python3_EXECUTABLE path we can use. + find_package(Python3 REQUIRED) + # Actually split the file + # Keeps the output in the build dir to not pollute the main dir + execute_process(COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/split.py" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ERROR_VARIABLE _httplib_split_error + ) + if(_httplib_split_error) + message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}") + endif() + + # split.py puts output in "out" + set(_httplib_build_includedir "${CMAKE_CURRENT_BINARY_DIR}/out") + # This will automatically be either static or shared based on the value of BUILD_SHARED_LIBS + add_library(${PROJECT_NAME} "${_httplib_build_includedir}/httplib.cc") + target_sources(${PROJECT_NAME} + PUBLIC + $ + $ + ) + set_target_properties(${PROJECT_NAME} + PROPERTIES + VERSION ${${PROJECT_NAME}_VERSION} + SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}" + ) +else() + # This is for header-only. + set(_INTERFACE_OR_PUBLIC INTERFACE) + add_library(${PROJECT_NAME} INTERFACE) + set(_httplib_build_includedir "${CMAKE_CURRENT_SOURCE_DIR}") +endif() +# Lets you address the target with httplib::httplib +# Only useful if building in-tree, versus using it from an installation. +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +# Require C++11 +target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11) + +target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC} + $ + $ +) + +# Always require threads +target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} + Threads::Threads + # Needed for Windows libs on Mingw, as the pragma comment(lib, "xyz") aren't triggered. + $<$:ws2_32> + $<$:crypt32> + # Needed for API from MacOS Security framework + "$<$,$,$>:-framework CoreFoundation -framework Security>" + # Can't put multiple targets in a single generator expression or it bugs out. + $<$:Brotli::common> + $<$:Brotli::encoder> + $<$:Brotli::decoder> + $<$:ZLIB::ZLIB> + $<$:OpenSSL::SSL> + $<$:OpenSSL::Crypto> +) + +# Set the definitions to enable optional features +target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} + $<$:CPPHTTPLIB_NO_EXCEPTIONS> + $<$:CPPHTTPLIB_BROTLI_SUPPORT> + $<$:CPPHTTPLIB_ZLIB_SUPPORT> + $<$:CPPHTTPLIB_OPENSSL_SUPPORT> + $<$,$,$>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN> +) + +# CMake configuration files installation directory +set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + +include(CMakePackageConfigHelpers) + +# Configures the meta-file httplibConfig.cmake.in to replace variables with paths/values/etc. +configure_package_config_file("cmake/${PROJECT_NAME}Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}" + # Passes the includedir install path + PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR +) + +if(HTTPLIB_COMPILE) + write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" + # Example: if you find_package(httplib 0.5.4) + # then anything >= 0.5.4 and < 0.6 is accepted + COMPATIBILITY SameMinorVersion + ) +else() + write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" + # Example: if you find_package(httplib 0.5.4) + # then anything >= 0.5.4 and < 0.6 is accepted + COMPATIBILITY SameMinorVersion + # Tells Cmake that it's a header-only lib + # Mildly useful for end-users :) + ARCH_INDEPENDENT + ) +endif() + +if(HTTPLIB_INSTALL) + # Creates the export httplibTargets.cmake + # This is strictly what holds compilation requirements + # and linkage information (doesn't find deps though). + install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets) + + install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + # Install it so it can be used later by the httplibConfig.cmake file. + # Put it in the same dir as our config file instead of a global path so we don't potentially stomp on other packages. + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindBrotli.cmake" + DESTINATION ${_TARGET_INSTALL_CMAKEDIR} + ) + + # NOTE: This path changes depending on if it's on Windows or Linux + install(EXPORT httplibTargets + # Puts the targets into the httplib namespace + # So this makes httplib::httplib linkable after doing find_package(httplib) + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${_TARGET_INSTALL_CMAKEDIR} + ) + + # Install documentation & license + # ex: /usr/share/doc/httplib/README.md and /usr/share/licenses/httplib/LICENSE + install(FILES "README.md" DESTINATION "${CMAKE_INSTALL_DOCDIR}") + install(FILES "LICENSE" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME}") +endif() + +if(HTTPLIB_TEST) + include(CTest) + add_subdirectory(test) +endif() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..3e5ed359a2bb16f52c383745c25f14bb7a81c9e4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 yhirose + +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. + diff --git a/cmake/FindBrotli.cmake b/cmake/FindBrotli.cmake new file mode 100644 index 0000000000000000000000000000000000000000..2048e554994ad5d99801657346b1abcb4ce745f0 --- /dev/null +++ b/cmake/FindBrotli.cmake @@ -0,0 +1,168 @@ +# A simple FindBrotli package for Cmake's find_package function. +# Note: This find package doesn't have version support, as the version file doesn't seem to be installed on most systems. +# +# If you want to find the static packages instead of shared (the default), define BROTLI_USE_STATIC_LIBS as TRUE. +# The targets will have the same names, but it will use the static libs. +# +# Valid find_package COMPONENTS names: "decoder", "encoder", and "common" +# Note that if you're requiring "decoder" or "encoder", then "common" will be automatically added as required. +# +# Defines the libraries (if found): Brotli::decoder, Brotli::encoder, Brotli::common +# and the includes path variable: Brotli_INCLUDE_DIR +# +# If it's failing to find the libraries, try setting BROTLI_ROOT_DIR to the folder containing your library & include dir. + +# If they asked for a specific version, warn/fail since we don't support it. +# TODO: if they start distributing the version somewhere, implement finding it. +# See https://github.com/google/brotli/issues/773#issuecomment-579133187 +if(Brotli_FIND_VERSION) + set(_brotli_version_error_msg "FindBrotli.cmake doesn't have version support!") + # If the package is required, throw a fatal error + # Otherwise, if not running quietly, we throw a warning + if(Brotli_FIND_REQUIRED) + message(FATAL_ERROR "${_brotli_version_error_msg}") + elseif(NOT Brotli_FIND_QUIETLY) + message(WARNING "${_brotli_version_error_msg}") + endif() +endif() + +# Since both decoder & encoder require the common lib, force its requirement.. +# if the user is requiring either of those other libs. +if(Brotli_FIND_REQUIRED_decoder OR Brotli_FIND_REQUIRED_encoder) + set(Brotli_FIND_REQUIRED_common TRUE) +endif() + +# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES +# Credit to FindOpenSSL.cmake for this +if(BROTLI_USE_STATIC_LIBS) + set(_brotli_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() +endif() + +# Make PkgConfig optional, since some users (mainly Windows) don't have it. +# But it's a lot more clean than manually using find_library. +find_package(PkgConfig QUIET) + +# Only used if the PkgConfig libraries aren't used. +find_path(Brotli_INCLUDE_DIR + NAMES + "brotli/decode.h" + "brotli/encode.h" + HINTS + ${BROTLI_ROOT_DIR} + PATH_SUFFIXES + "include" + "includes" + DOC "The path to Brotli's include directory." +) +# Hides this var from the GUI +mark_as_advanced(Brotli_INCLUDE_DIR) + +# Just used for PkgConfig stuff in the loop below +set(_brotli_stat_str "") +if(BROTLI_USE_STATIC_LIBS) + set(_brotli_stat_str "_STATIC") +endif() + +# Each string here is "ComponentName;LiteralName" (the semi-colon is a delimiter) +foreach(_listvar "common;common" "decoder;dec" "encoder;enc") + # Split the component name and literal library name from the listvar + list(GET _listvar 0 _component_name) + list(GET _listvar 1 _libname) + + # NOTE: We can't rely on PkgConf for static libs since the upstream static lib support is broken + # See https://github.com/google/brotli/issues/795 + # TODO: whenever their issue is fixed upstream, remove this "AND NOT BROTLI_USE_STATIC_LIBS" check + if(PKG_CONFIG_FOUND AND NOT BROTLI_USE_STATIC_LIBS) + # These need to be GLOBAL for MinGW when making ALIAS libraries against them. + # Have to postfix _STATIC on the name to tell PkgConfig to find the static libs. + pkg_check_modules(Brotli_${_component_name}${_brotli_stat_str} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname}) + endif() + + # Check if the target was already found by Pkgconf + if(TARGET PkgConfig::Brotli_${_component_name}${_brotli_stat_str}) + # ALIAS since we don't want the PkgConfig namespace on the Cmake library (for end-users) + add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str}) + + # Tells HANDLE_COMPONENTS we found the component + set(Brotli_${_component_name}_FOUND TRUE) + if(Brotli_FIND_REQUIRED_${_component_name}) + # If the lib is required, we can add its literal path as a required var for FindPackageHandleStandardArgs + # Since it won't accept the PkgConfig targets + if(BROTLI_USE_STATIC_LIBS) + list(APPEND _brotli_req_vars "Brotli_${_component_name}_STATIC_LIBRARIES") + else() + list(APPEND _brotli_req_vars "Brotli_${_component_name}_LINK_LIBRARIES") + endif() + endif() + + # Skip searching for the libs with find_library since it was already found by Pkgconf + continue() + endif() + + if(Brotli_FIND_REQUIRED_${_component_name}) + # If it's required, we can set the name used in find_library as a required var for FindPackageHandleStandardArgs + list(APPEND _brotli_req_vars "Brotli_${_component_name}") + endif() + + list(APPEND _brotli_lib_names + "brotli${_libname}" + "libbrotli${_libname}" + ) + if(BROTLI_USE_STATIC_LIBS) + # Postfix "-static" to the libnames since we're looking for static libs + list(TRANSFORM _brotli_lib_names APPEND "-static") + endif() + + find_library(Brotli_${_component_name} + NAMES ${_brotli_lib_names} + HINTS ${BROTLI_ROOT_DIR} + PATH_SUFFIXES + "lib" + "lib64" + "libs" + "libs64" + "lib/x86_64-linux-gnu" + ) + # Hide the library variable from the Cmake GUI + mark_as_advanced(Brotli_${_component_name}) + + # Unset since otherwise it'll stick around for the next loop and break things + unset(_brotli_lib_names) + + # Check if find_library found the library + if(Brotli_${_component_name}) + # Tells HANDLE_COMPONENTS we found the component + set(Brotli_${_component_name}_FOUND TRUE) + + add_library("Brotli::${_component_name}" UNKNOWN IMPORTED) + # Attach the literal library and include dir to the IMPORTED target for the end-user + set_target_properties("Brotli::${_component_name}" PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Brotli_INCLUDE_DIR}" + IMPORTED_LOCATION "${Brotli_${_component_name}}" + ) + else() + # Tells HANDLE_COMPONENTS we found the component + set(Brotli_${_component_name}_FOUND FALSE) + endif() +endforeach() + +include(FindPackageHandleStandardArgs) +# Sets Brotli_FOUND, and fails the find_package(Brotli) call if it was REQUIRED but missing libs. +find_package_handle_standard_args(Brotli + FOUND_VAR + Brotli_FOUND + REQUIRED_VARS + Brotli_INCLUDE_DIR + ${_brotli_req_vars} + HANDLE_COMPONENTS +) + +# Restore the original find library ordering +if(BROTLI_USE_STATIC_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${_brotli_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +endif() diff --git a/cmake/httplibConfig.cmake.in b/cmake/httplibConfig.cmake.in new file mode 100644 index 0000000000000000000000000000000000000000..93dff323de1dfd3fc23e66cd38be0d5471ec7db1 --- /dev/null +++ b/cmake/httplibConfig.cmake.in @@ -0,0 +1,84 @@ +# Generates a macro to auto-configure everything +@PACKAGE_INIT@ + +# Setting these here so they're accessible after install. +# Might be useful for some users to check which settings were used. +set(HTTPLIB_IS_USING_OPENSSL @HTTPLIB_IS_USING_OPENSSL@) +set(HTTPLIB_IS_USING_ZLIB @HTTPLIB_IS_USING_ZLIB@) +set(HTTPLIB_IS_COMPILED @HTTPLIB_COMPILE@) +set(HTTPLIB_IS_USING_BROTLI @HTTPLIB_IS_USING_BROTLI@) +set(HTTPLIB_VERSION @PROJECT_VERSION@) + +include(CMakeFindDependencyMacro) + +# We add find_dependency calls here to not make the end-user have to call them. +find_dependency(Threads) +if(@HTTPLIB_IS_USING_OPENSSL@) + # OpenSSL COMPONENTS were added in Cmake v3.11 + if(CMAKE_VERSION VERSION_LESS "3.11") + find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@) + else() + # Once the COMPONENTS were added, they were made optional when not specified. + # Since we use both, we need to search for both. + find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL) + endif() +endif() +if(@HTTPLIB_IS_USING_ZLIB@) + find_dependency(ZLIB) +endif() + +if(@HTTPLIB_IS_USING_BROTLI@) + # Needed so we can use our own FindBrotli.cmake in this file. + # Note that the FindBrotli.cmake file is installed in the same dir as this file. + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@) + find_dependency(Brotli COMPONENTS common encoder decoder) +endif() + +# Mildly useful for end-users +# Not really recommended to be used though +set_and_check(HTTPLIB_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@") +# Lets the end-user find the header path with the header appended +# This is helpful if you're using Cmake's pre-compiled header feature +set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h") + +# Consider each library support as a "component" +set(httplib_OpenSSL_FOUND @HTTPLIB_IS_USING_OPENSSL@) +set(httplib_ZLIB_FOUND @HTTPLIB_IS_USING_ZLIB@) +set(httplib_Brotli_FOUND @HTTPLIB_IS_USING_BROTLI@) + +check_required_components(httplib) + +# Brings in the target library, but only if all required components are found +if(NOT DEFINED httplib_FOUND OR httplib_FOUND) + include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake") +endif() + +# Outputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib) +include(FindPackageMessage) +if(TARGET httplib::httplib) + set(HTTPLIB_FOUND TRUE) + + # Since the compiled version has a lib, show that in the message + if(@HTTPLIB_COMPILE@) + # The list of configurations is most likely just 1 unless they installed a debug & release + get_target_property(_httplib_configs httplib::httplib "IMPORTED_CONFIGURATIONS") + # Need to loop since the "IMPORTED_LOCATION" property isn't want we want. + # Instead, we need to find the IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG which has the lib path. + foreach(_httplib_conf "${_httplib_configs}") + # Grab the path to the lib and sets it to HTTPLIB_LIBRARY + get_target_property(HTTPLIB_LIBRARY httplib::httplib "IMPORTED_LOCATION_${_httplib_conf}") + # Check if we found it + if(HTTPLIB_LIBRARY) + break() + endif() + endforeach() + + unset(_httplib_configs) + unset(_httplib_conf) + + find_package_message(httplib "Found httplib: ${HTTPLIB_LIBRARY} (found version \"${HTTPLIB_VERSION}\")" "[${HTTPLIB_LIBRARY}][${HTTPLIB_HEADER_PATH}]") + else() + find_package_message(httplib "Found httplib: ${HTTPLIB_HEADER_PATH} (found version \"${HTTPLIB_VERSION}\")" "[${HTTPLIB_HEADER_PATH}]") + endif() +endif() diff --git a/example/Dockerfile.hello b/example/Dockerfile.hello new file mode 100644 index 0000000000000000000000000000000000000000..d1188aecbb435b1d903a2c386ccabe966e671162 --- /dev/null +++ b/example/Dockerfile.hello @@ -0,0 +1,12 @@ +FROM alpine as builder +WORKDIR /src/example +RUN apk add g++ make openssl-dev zlib-dev brotli-dev +COPY ./httplib.h /src +COPY ./example/hello.cc /src/example +COPY ./example/Makefile /src/example +RUN make hello + +FROM alpine +RUN apk --no-cache add brotli libstdc++ +COPY --from=builder /src/example/hello /bin/hello +CMD ["/bin/hello"] diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..64a35885f2bf55665c86f07613d52fe39a504b26 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,59 @@ +#CXX = clang++ +CXXFLAGS = -O2 -std=c++11 -I.. -Wall -Wextra -pthread + +PREFIX = /usr/local +#PREFIX = $(shell brew --prefix) + +OPENSSL_DIR = $(PREFIX)/opt/openssl@3 +OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto + +ifneq ($(OS), Windows_NT) + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S), Darwin) + OPENSSL_SUPPORT += -framework CoreFoundation -framework Security + endif +endif + +ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz + +BROTLI_DIR = $(PREFIX)/opt/brotli +BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec + +all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark issue + +server : server.cc ../httplib.h Makefile + $(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +client : client.cc ../httplib.h Makefile + $(CXX) -o client $(CXXFLAGS) client.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +hello : hello.cc ../httplib.h Makefile + $(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +simplecli : simplecli.cc ../httplib.h Makefile + $(CXX) -o simplecli $(CXXFLAGS) simplecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +simplesvr : simplesvr.cc ../httplib.h Makefile + $(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +upload : upload.cc ../httplib.h Makefile + $(CXX) -o upload $(CXXFLAGS) upload.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +redirect : redirect.cc ../httplib.h Makefile + $(CXX) -o redirect $(CXXFLAGS) redirect.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +ssesvr : ssesvr.cc ../httplib.h Makefile + $(CXX) -o ssesvr $(CXXFLAGS) ssesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +ssecli : ssecli.cc ../httplib.h Makefile + $(CXX) -o ssecli $(CXXFLAGS) ssecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +benchmark : benchmark.cc ../httplib.h Makefile + $(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) + +pem: + openssl genrsa 2048 > key.pem + openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem + +clean: + rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark *.pem diff --git a/example/benchmark.cc b/example/benchmark.cc new file mode 100644 index 0000000000000000000000000000000000000000..433cc675c4571f82d409f91c706f728b173892e0 --- /dev/null +++ b/example/benchmark.cc @@ -0,0 +1,33 @@ +#include +#include +#include + +using namespace std; + +struct StopWatch { + StopWatch(const string &label) : label_(label) { + start_ = chrono::system_clock::now(); + } + ~StopWatch() { + auto end = chrono::system_clock::now(); + auto diff = end - start_; + auto count = chrono::duration_cast(diff).count(); + cout << label_ << ": " << count << " millisec." << endl; + } + string label_; + chrono::system_clock::time_point start_; +}; + +int main(void) { + string body(1024 * 5, 'a'); + + httplib::Client cli("httpbin.org", 80); + + for (int i = 0; i < 3; i++) { + StopWatch sw(to_string(i).c_str()); + auto res = cli.Post("/post", body, "application/octet-stream"); + assert(res->status == httplib::StatusCode::OK_200); + } + + return 0; +} diff --git a/example/ca-bundle.crt b/example/ca-bundle.crt new file mode 100644 index 0000000000000000000000000000000000000000..7d61eb5cf1b0e6f0fbaa6b3585cc3d17c1009334 --- /dev/null +++ b/example/ca-bundle.crt @@ -0,0 +1,3401 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Jan 22 14:14:40 2019 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.27. +## SHA256: 18372117493b5b7ec006c31d966143fc95a9464a2b5f8d5188e23c5557b2292d +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ +5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn +vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj +CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil +e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR +OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI +CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 +48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi +trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 +qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB +AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC +ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA +A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz ++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj +f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN +kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk +CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF +URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb +CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h +oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV +IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm +66+KAQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl +OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV +MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF +JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G3 +================================== +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y +olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t +x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy +EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K +Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur +mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5 +1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp +07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo +FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE +41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu +yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq +KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1 +v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA +8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b +8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r +mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq +1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI +JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV +tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk= +-----END CERTIFICATE----- + +Staat der Nederlanden EV Root CA +================================ +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M +MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl +cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk +SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW +O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r +0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 +Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV +XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr +08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV +0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd +74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx +fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa +ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu +c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq +5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN +b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN +f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi +5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 +WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK +DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy +eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +Certinomis - Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg +LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx +EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD +ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos +P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo +d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap +z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00 +8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x +RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE +6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t +FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV +PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH +i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj +YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I +6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV +WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw +Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX +lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ +y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9 +Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng +DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi +I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM +cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr +hkIGuUE= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +LuxTrust Global Root 2 +====================== +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG +A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh +bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW +MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm +Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2 +xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC +wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm +1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm +FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF +wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/ +a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U +ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ +MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB +/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5 +Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ +FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN +H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW +7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu +ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA +VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR +TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt +/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc +7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I +iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +GDCA TrustAUTH R5 ROOT +====================== +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw +BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD +DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow +YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs +AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p +OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr +pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ +9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ +xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM +R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ +D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 +oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx +9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 +H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 +6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd ++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ +HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD +F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ +8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv +/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT +aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +TrustCor RootCert CA-1 +====================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx +MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu +YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe +VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy +dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq +jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 +pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 +JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h +gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw +/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j +BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 +mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C +qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P +3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +TrustCor RootCert CA-2 +====================== +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w +DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT +eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 +eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy +MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h +bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 +IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb +ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk +RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 +oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb +XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 +/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q +jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP +eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg +rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU +2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h +Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp +kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv +2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 +S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw +PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv +DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU +RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE +xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX +RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ +-----END CERTIFICATE----- + +TrustCor ECA-1 +============== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw +N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 +MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y +IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR +MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 +xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc +p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ +fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj +YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL +f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u +/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs +J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC +jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== +-----END CERTIFICATE----- + +SSL.com Root Certification Authority RSA +======================================== +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM +BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x +MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw +MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM +LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C +Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 +P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge +oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp +k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z +fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ +gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 +UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 +1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s +bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr +dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf +ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl +u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq +erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj +MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ +vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI +Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y +wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI +WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +SSL.com Root Certification Authority ECC +======================================== +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv +BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy +MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO +BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ +8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR +hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT +jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW +e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z +5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority RSA R2 +============================================== +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w +DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u +MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD +VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh +hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w +cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO +Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ +B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh +CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim +9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto +RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm +JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 ++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp +qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 +++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx +Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G +guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz +OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 +CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq +lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR +rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 +hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX +9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority ECC +=========================================== +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy +BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw +MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM +LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy +3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O +BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe +5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ +N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm +m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx +9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r +aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW +r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM +LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly +4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr +06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om +3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu +JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM +BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv +fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm +ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b +gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq +4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr +tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo +pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0 +sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql +CFF1pkgl +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk +k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo +7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI +m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm +dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu +ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz +cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl +aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy +5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM +BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ ++YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw +c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da +WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r +n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu +Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ +7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs +gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld +o/DUhgkC +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU +Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP +0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0 +glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa +KaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa +6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV +2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI +N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x +zPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- diff --git a/example/client.cc b/example/client.cc new file mode 100644 index 0000000000000000000000000000000000000000..a9b0fc0ac352097e0e92b26ffdca7173787121d6 --- /dev/null +++ b/example/client.cc @@ -0,0 +1,41 @@ +// +// client.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +#include + +#define CA_CERT_FILE "./ca-bundle.crt" + +using namespace std; + +int main(void) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + httplib::SSLClient cli("localhost", 8080); + // httplib::SSLClient cli("google.com"); + // httplib::SSLClient cli("www.youtube.com"); + cli.set_ca_cert_path(CA_CERT_FILE); + cli.enable_server_certificate_verification(true); +#else + httplib::Client cli("localhost", 8080); +#endif + + if (auto res = cli.Get("/hi")) { + cout << res->status << endl; + cout << res->get_header_value("Content-Type") << endl; + cout << res->body << endl; + } else { + cout << "error code: " << res.error() << std::endl; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto result = cli.get_openssl_verify_result(); + if (result) { + cout << "verify error: " << X509_verify_cert_error_string(result) << endl; + } +#endif + } + + return 0; +} diff --git a/example/client.vcxproj b/example/client.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..1abb7739106f846571f9b2cb31a1606d12ba5f7a --- /dev/null +++ b/example/client.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6DB1FC63-B153-4279-92B7-D8A11AF285D6} + Win32Proj + client + 10.0.15063.0 + + + + Application + true + Unicode + v141 + + + Application + true + Unicode + v141 + + + Application + false + true + Unicode + v141 + + + Application + false + true + Unicode + v141 + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)_obj\ + + + true + $(Platform)\$(Configuration)\$(ProjectName)_obj\ + + + false + $(Configuration)\$(ProjectName)_obj\ + + + false + $(Platform)\$(Configuration)\$(ProjectName)_obj\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + Ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + Ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + true + true + Ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + true + true + Ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/example/example.sln b/example/example.sln new file mode 100644 index 0000000000000000000000000000000000000000..81a3e787c8e577d0ff7f86f92151a071e29d600a --- /dev/null +++ b/example/example.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2047 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "server", "server.vcxproj", "{864CD288-050A-4C8B-9BEF-3048BD876C5B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "client", "client.vcxproj", "{6DB1FC63-B153-4279-92B7-D8A11AF285D6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{280E605F-0CB8-4336-8D9F-CE50A9472AE2}" + ProjectSection(SolutionItems) = preProject + ..\README.md = ..\README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Debug|Win32.ActiveCfg = Debug|Win32 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Debug|Win32.Build.0 = Debug|Win32 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Debug|x64.ActiveCfg = Debug|x64 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Debug|x64.Build.0 = Debug|x64 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Release|Win32.ActiveCfg = Release|Win32 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Release|Win32.Build.0 = Release|Win32 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Release|x64.ActiveCfg = Release|x64 + {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Release|x64.Build.0 = Release|x64 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Debug|Win32.Build.0 = Debug|Win32 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Debug|x64.ActiveCfg = Debug|x64 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Debug|x64.Build.0 = Debug|x64 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Release|Win32.ActiveCfg = Release|Win32 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Release|Win32.Build.0 = Release|Win32 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Release|x64.ActiveCfg = Release|x64 + {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7097C9E4-07F8-48C6-A888-BBA9EBB5D17D} + EndGlobalSection +EndGlobal diff --git a/example/hello.cc b/example/hello.cc new file mode 100644 index 0000000000000000000000000000000000000000..38d25a62b89209a20ce2b3d8e4c7c3ac9543bb8e --- /dev/null +++ b/example/hello.cc @@ -0,0 +1,19 @@ +// +// hello.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +using namespace httplib; + +int main(void) { + Server svr; + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + svr.listen("0.0.0.0", 8080); +} diff --git a/example/redirect.cc b/example/redirect.cc new file mode 100644 index 0000000000000000000000000000000000000000..b1f39396a52423654b6f30252a423bf3ff808a93 --- /dev/null +++ b/example/redirect.cc @@ -0,0 +1,60 @@ +// +// redirect.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include + +#define SERVER_CERT_FILE "./cert.pem" +#define SERVER_PRIVATE_KEY_FILE "./key.pem" + +using namespace httplib; + +int main(void) { + // HTTP server + Server http; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLServer https(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); +#endif + + http.Get("/test", [](const Request & /*req*/, Response &res) { + res.set_content("Test\n", "text/plain"); + }); + + http.set_error_handler([](const Request & /*req*/, Response &res) { + res.set_redirect("https://localhost:8081/"); + }); + + // HTTPS server +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + https.Get("/", [=](const Request & /*req*/, Response &res) { + res.set_redirect("/hi"); + }); + + https.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!\n", "text/plain"); + }); + + https.Get("/stop", [&](const Request & /*req*/, Response & /*res*/) { + https.stop(); + http.stop(); + }); +#endif + + // Run servers + auto httpThread = std::thread([&]() { http.listen("localhost", 8080); }); + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto httpsThread = std::thread([&]() { https.listen("localhost", 8081); }); +#endif + + httpThread.join(); + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + httpsThread.join(); +#endif + + return 0; +} diff --git a/example/server.cc b/example/server.cc new file mode 100644 index 0000000000000000000000000000000000000000..1c347f5f9cb7df52b23869885b6886a611500d93 --- /dev/null +++ b/example/server.cc @@ -0,0 +1,113 @@ +// +// sample.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +#include +#include + +#define SERVER_CERT_FILE "./cert.pem" +#define SERVER_PRIVATE_KEY_FILE "./key.pem" + +using namespace httplib; + +std::string dump_headers(const Headers &headers) { + std::string s; + char buf[BUFSIZ]; + + for (auto it = headers.begin(); it != headers.end(); ++it) { + const auto &x = *it; + snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); + s += buf; + } + + return s; +} + +std::string log(const Request &req, const Response &res) { + std::string s; + char buf[BUFSIZ]; + + s += "================================\n"; + + snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(), + req.version.c_str(), req.path.c_str()); + s += buf; + + std::string query; + for (auto it = req.params.begin(); it != req.params.end(); ++it) { + const auto &x = *it; + snprintf(buf, sizeof(buf), "%c%s=%s", + (it == req.params.begin()) ? '?' : '&', x.first.c_str(), + x.second.c_str()); + query += buf; + } + snprintf(buf, sizeof(buf), "%s\n", query.c_str()); + s += buf; + + s += dump_headers(req.headers); + + s += "--------------------------------\n"; + + snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str()); + s += buf; + s += dump_headers(res.headers); + s += "\n"; + + if (!res.body.empty()) { s += res.body; } + + s += "\n"; + + return s; +} + +int main(void) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); +#else + Server svr; +#endif + + if (!svr.is_valid()) { + printf("server has an error...\n"); + return -1; + } + + svr.Get("/", [=](const Request & /*req*/, Response &res) { + res.set_redirect("/hi"); + }); + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!\n", "text/plain"); + }); + + svr.Get("/slow", [](const Request & /*req*/, Response &res) { + std::this_thread::sleep_for(std::chrono::seconds(2)); + res.set_content("Slow...\n", "text/plain"); + }); + + svr.Get("/dump", [](const Request &req, Response &res) { + res.set_content(dump_headers(req.headers), "text/plain"); + }); + + svr.Get("/stop", + [&](const Request & /*req*/, Response & /*res*/) { svr.stop(); }); + + svr.set_error_handler([](const Request & /*req*/, Response &res) { + const char *fmt = "

Error Status: %d

"; + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), fmt, res.status); + res.set_content(buf, "text/html"); + }); + + svr.set_logger([](const Request &req, const Response &res) { + printf("%s", log(req, res).c_str()); + }); + + svr.listen("localhost", 8080); + + return 0; +} diff --git a/example/server.vcxproj b/example/server.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..31ff203747dc9d6eff07df575cdd485e3c5803af --- /dev/null +++ b/example/server.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {864CD288-050A-4C8B-9BEF-3048BD876C5B} + Win32Proj + sample + 10.0.15063.0 + + + + Application + true + Unicode + v141 + + + Application + true + Unicode + v141 + + + Application + false + true + Unicode + v141 + + + Application + false + true + Unicode + v141 + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)_obj\ + + + true + $(Platform)\$(Configuration)\$(ProjectName)_obj\ + + + false + $(Configuration)\$(ProjectName)_obj\ + + + false + $(Platform)\$(Configuration)\$(ProjectName)_obj\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + .. + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/example/simplecli.cc b/example/simplecli.cc new file mode 100644 index 0000000000000000000000000000000000000000..b005e4084803a2499a0ae7e574e9c5b436513d55 --- /dev/null +++ b/example/simplecli.cc @@ -0,0 +1,29 @@ +// +// simplecli.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +#include + +using namespace std; + +int main(void) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto scheme_host_port = "https://localhost:8080"; +#else + auto scheme_host_port = "http://localhost:8080"; +#endif + + if (auto res = httplib::Client(scheme_host_port).Get("/hi")) { + cout << res->status << endl; + cout << res->get_header_value("Content-Type") << endl; + cout << res->body << endl; + } else { + cout << res.error() << endl; + } + + return 0; +} diff --git a/example/simplesvr.cc b/example/simplesvr.cc new file mode 100644 index 0000000000000000000000000000000000000000..17a9e98597bc2abe77d7e5af885b3f9b3a350eb1 --- /dev/null +++ b/example/simplesvr.cc @@ -0,0 +1,135 @@ +// +// simplesvr.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +#include +#include + +#define SERVER_CERT_FILE "./cert.pem" +#define SERVER_PRIVATE_KEY_FILE "./key.pem" + +using namespace httplib; +using namespace std; + +string dump_headers(const Headers &headers) { + string s; + char buf[BUFSIZ]; + + for (const auto &x : headers) { + snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); + s += buf; + } + + return s; +} + +string dump_multipart_files(const MultipartFormDataMap &files) { + string s; + char buf[BUFSIZ]; + + s += "--------------------------------\n"; + + for (const auto &x : files) { + const auto &name = x.first; + const auto &file = x.second; + + snprintf(buf, sizeof(buf), "name: %s\n", name.c_str()); + s += buf; + + snprintf(buf, sizeof(buf), "filename: %s\n", file.filename.c_str()); + s += buf; + + snprintf(buf, sizeof(buf), "content type: %s\n", file.content_type.c_str()); + s += buf; + + snprintf(buf, sizeof(buf), "text length: %zu\n", file.content.size()); + s += buf; + + s += "----------------\n"; + } + + return s; +} + +string log(const Request &req, const Response &res) { + string s; + char buf[BUFSIZ]; + + s += "================================\n"; + + snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(), + req.version.c_str(), req.path.c_str()); + s += buf; + + string query; + for (auto it = req.params.begin(); it != req.params.end(); ++it) { + const auto &x = *it; + snprintf(buf, sizeof(buf), "%c%s=%s", + (it == req.params.begin()) ? '?' : '&', x.first.c_str(), + x.second.c_str()); + query += buf; + } + snprintf(buf, sizeof(buf), "%s\n", query.c_str()); + s += buf; + + s += dump_headers(req.headers); + s += dump_multipart_files(req.files); + + s += "--------------------------------\n"; + + snprintf(buf, sizeof(buf), "%d\n", res.status); + s += buf; + s += dump_headers(res.headers); + + return s; +} + +int main(int argc, const char **argv) { + if (argc > 1 && string("--help") == argv[1]) { + cout << "usage: simplesvr [PORT] [DIR]" << endl; + return 1; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); +#else + Server svr; +#endif + + svr.Post("/multipart", [](const Request &req, Response &res) { + auto body = dump_headers(req.headers) + dump_multipart_files(req.files); + + res.set_content(body, "text/plain"); + }); + + svr.set_error_handler([](const Request & /*req*/, Response &res) { + const char *fmt = "

Error Status: %d

"; + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), fmt, res.status); + res.set_content(buf, "text/html"); + }); + + svr.set_logger( + [](const Request &req, const Response &res) { cout << log(req, res); }); + + auto port = 8080; + if (argc > 1) { port = atoi(argv[1]); } + + auto base_dir = "./"; + if (argc > 2) { base_dir = argv[2]; } + + if (!svr.set_mount_point("/", base_dir)) { + cout << "The specified base directory doesn't exist..."; + return 1; + } + + cout << "The server started at port " << port << "..." << endl; + + svr.listen("localhost", port); + + return 0; +} diff --git a/example/upload.cc b/example/upload.cc new file mode 100644 index 0000000000000000000000000000000000000000..1e4f242afadba0c7dc95b007cc39830a9d074ed1 --- /dev/null +++ b/example/upload.cc @@ -0,0 +1,61 @@ +// +// upload.cc +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#include +#include +#include +using namespace httplib; +using namespace std; + +const char *html = R"( +
+ + + +
+ +)"; + +int main(void) { + Server svr; + + svr.Get("/", [](const Request & /*req*/, Response &res) { + res.set_content(html, "text/html"); + }); + + svr.Post("/post", [](const Request &req, Response &res) { + auto image_file = req.get_file_value("image_file"); + auto text_file = req.get_file_value("text_file"); + + cout << "image file length: " << image_file.content.length() << endl + << "image file name: " << image_file.filename << endl + << "text file length: " << text_file.content.length() << endl + << "text file name: " << text_file.filename << endl; + + { + ofstream ofs(image_file.filename, ios::binary); + ofs << image_file.content; + } + { + ofstream ofs(text_file.filename); + ofs << text_file.content; + } + + res.set_content("done", "text/plain"); + }); + + svr.listen("localhost", 1234); +} diff --git a/example/uploader.sh b/example/uploader.sh new file mode 100644 index 0000000000000000000000000000000000000000..4382ae6217593ca68b409d969338c5ef9d6bbea5 --- /dev/null +++ b/example/uploader.sh @@ -0,0 +1,6 @@ +#/usr/bin/env bash +for i in {1..1000000} +do + echo "#### $i ####" + curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null +done diff --git a/httplib.h b/httplib.h new file mode 100644 index 0000000000000000000000000000000000000000..a7a561333e2966944f443c8fe0ec603101a12ba6 --- /dev/null +++ b/httplib.h @@ -0,0 +1,9790 @@ +// +// httplib.h +// +// Copyright (c) 2024 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPHTTPLIB_HTTPLIB_H +#define CPPHTTPLIB_HTTPLIB_H + +#define CPPHTTPLIB_VERSION "0.15.3" + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH +#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT +#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_RANGE_MAX_COUNT +#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY false +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ +#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ + ? std::thread::hardware_concurrency() - 1 \ + : 0)) +#endif + +#ifndef CPPHTTPLIB_RECV_FLAGS +#define CPPHTTPLIB_RECV_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_SEND_FLAGS +#define CPPHTTPLIB_SEND_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_LISTEN_BACKLOG +#define CPPHTTPLIB_LISTEN_BACKLOG 5 +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#if _MSC_VER < 1900 +#error Sorry, Visual Studio versions prior to 2015 are not supported +#endif + +#pragma comment(lib, "ws2_32.lib") + +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = long; +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#if !defined(_AIX) && !defined(__MVS__) +#include +#endif +#ifdef __MVS__ +#include +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#endif +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +using socket_t = int; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#ifdef _WIN32 +#include + +// these are defined in wincrypt.h and it breaks compilation if BoringSSL is +// used +#undef X509_NAME +#undef X509_CERT_PAIR +#undef X509_EXTENSIONS +#undef PKCS7_SIGNER_INFO + +#ifdef _MSC_VER +#pragma comment(lib, "crypt32.lib") +#endif +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#include +#if TARGET_OS_OSX +#include +#include +#endif // TARGET_OS_OSX +#endif // _WIN32 + +#include +#include +#include +#include + +#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) +#include +#endif + +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x30000000L +#error Sorry, OpenSSL versions prior to 3.0.0 are not supported +#endif + +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +#include +#include +#endif + +/* + * Declaration + */ +namespace httplib { + +namespace detail { + +/* + * Backport std::make_unique from C++14. + * + * NOTE: This code came up with the following stackoverflow post: + * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique + * + */ + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(Args &&...args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(std::size_t n) { + typedef typename std::remove_extent::type RT; + return std::unique_ptr(new RT[n]); +} + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), + s2.end(), + [](unsigned char c1, unsigned char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } +}; + +// This is based on +// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". + +struct scope_exit { + explicit scope_exit(std::function &&f) + : exit_function(std::move(f)), execute_on_destruction{true} {} + + scope_exit(scope_exit &&rhs) noexcept + : exit_function(std::move(rhs.exit_function)), + execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } + + ~scope_exit() { + if (execute_on_destruction) { this->exit_function(); } + } + + void release() { this->execute_on_destruction = false; } + +private: + scope_exit(const scope_exit &) = delete; + void operator=(const scope_exit &) = delete; + scope_exit &operator=(scope_exit &&) = delete; + + std::function exit_function; + bool execute_on_destruction; +}; + +} // namespace detail + +enum StatusCode { + // Information responses + Continue_100 = 100, + SwitchingProtocol_101 = 101, + Processing_102 = 102, + EarlyHints_103 = 103, + + // Successful responses + OK_200 = 200, + Created_201 = 201, + Accepted_202 = 202, + NonAuthoritativeInformation_203 = 203, + NoContent_204 = 204, + ResetContent_205 = 205, + PartialContent_206 = 206, + MultiStatus_207 = 207, + AlreadyReported_208 = 208, + IMUsed_226 = 226, + + // Redirection messages + MultipleChoices_300 = 300, + MovedPermanently_301 = 301, + Found_302 = 302, + SeeOther_303 = 303, + NotModified_304 = 304, + UseProxy_305 = 305, + unused_306 = 306, + TemporaryRedirect_307 = 307, + PermanentRedirect_308 = 308, + + // Client error responses + BadRequest_400 = 400, + Unauthorized_401 = 401, + PaymentRequired_402 = 402, + Forbidden_403 = 403, + NotFound_404 = 404, + MethodNotAllowed_405 = 405, + NotAcceptable_406 = 406, + ProxyAuthenticationRequired_407 = 407, + RequestTimeout_408 = 408, + Conflict_409 = 409, + Gone_410 = 410, + LengthRequired_411 = 411, + PreconditionFailed_412 = 412, + PayloadTooLarge_413 = 413, + UriTooLong_414 = 414, + UnsupportedMediaType_415 = 415, + RangeNotSatisfiable_416 = 416, + ExpectationFailed_417 = 417, + ImATeapot_418 = 418, + MisdirectedRequest_421 = 421, + UnprocessableContent_422 = 422, + Locked_423 = 423, + FailedDependency_424 = 424, + TooEarly_425 = 425, + UpgradeRequired_426 = 426, + PreconditionRequired_428 = 428, + TooManyRequests_429 = 429, + RequestHeaderFieldsTooLarge_431 = 431, + UnavailableForLegalReasons_451 = 451, + + // Server error responses + InternalServerError_500 = 500, + NotImplemented_501 = 501, + BadGateway_502 = 502, + ServiceUnavailable_503 = 503, + GatewayTimeout_504 = 504, + HttpVersionNotSupported_505 = 505, + VariantAlsoNegotiates_506 = 506, + InsufficientStorage_507 = 507, + LoopDetected_508 = 508, + NotExtended_510 = 510, + NetworkAuthenticationRequired_511 = 511, +}; + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { +public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function is_writable; + std::function done; + std::function done_with_trailer; + std::ostream os; + +private: + class data_sink_streambuf final : public std::streambuf { + public: + explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) override { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = + std::function; + +using ContentProviderWithoutLength = + std::function; + +using ContentProviderResourceReleaser = std::function; + +struct MultipartFormDataProvider { + std::string name; + ContentProviderWithoutLength provider; + std::string filename; + std::string content_type; +}; +using MultipartFormDataProviderItems = std::vector; + +using ContentReceiverWithProgress = + std::function; + +using ContentReceiver = + std::function; + +using MultipartContentHeader = + std::function; + +class ContentReader { +public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(std::move(reader)), + multipart_reader_(std::move(multipart_reader)) {} + + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const { + return multipart_reader_(std::move(header), std::move(receiver)); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(std::move(receiver)); + } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + std::string local_addr; + int local_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + std::unordered_map path_params; + + // for client + ResponseHandler response_handler; + ContentReceiverWithProgress content_receiver; + Progress progress; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl = nullptr; +#endif + + bool has_header(const std::string &key) const; + std::string get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + size_t get_header_value_count(const std::string &key) const; + void set_header(const std::string &key, const std::string &val); + + bool has_param(const std::string &key) const; + std::string get_param_value(const std::string &key, size_t id = 0) const; + size_t get_param_value_count(const std::string &key) const; + + bool is_multipart_form_data() const; + + bool has_file(const std::string &key) const; + MultipartFormData get_file_value(const std::string &key) const; + std::vector get_file_values(const std::string &key) const; + + // private members... + size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; + size_t content_length_ = 0; + ContentProvider content_provider_; + bool is_chunked_content_provider_ = false; + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + std::string reason; + Headers headers; + std::string body; + std::string location; // Redirect location + + bool has_header(const std::string &key) const; + std::string get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + size_t get_header_value_count(const std::string &key) const; + void set_header(const std::string &key, const std::string &val); + + void set_redirect(const std::string &url, int status = StatusCode::Found_302); + void set_content(const char *s, size_t n, const std::string &content_type); + void set_content(const std::string &s, const std::string &content_type); + void set_content(std::string &&s, const std::string &content_type); + + void set_content_provider( + size_t length, const std::string &content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_chunked_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(content_provider_success_); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + ContentProviderResourceReleaser content_provider_resource_releaser_; + bool is_chunked_content_provider_ = false; + bool content_provider_success_ = false; +}; + +class Stream { +public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0; + virtual socket_t socket() const = 0; + + template + ssize_t write_format(const char *fmt, const Args &...args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +class TaskQueue { +public: + TaskQueue() = default; + virtual ~TaskQueue() = default; + + virtual bool enqueue(std::function fn) = 0; + virtual void shutdown() = 0; + + virtual void on_idle() {} +}; + +class ThreadPool final : public TaskQueue { +public: + explicit ThreadPool(size_t n, size_t mqr = 0) + : shutdown_(false), max_queued_requests_(mqr) { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } + } + + ThreadPool(const ThreadPool &) = delete; + ~ThreadPool() override = default; + + bool enqueue(std::function fn) override { + { + std::unique_lock lock(mutex_); + if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) { + return false; + } + jobs_.push_back(std::move(fn)); + } + + cond_.notify_one(); + return true; + } + + void shutdown() override { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); + + // Join... + for (auto &t : threads_) { + t.join(); + } + } + +private: + struct worker { + explicit worker(ThreadPool &pool) : pool_(pool) {} + + void operator()() { + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); + + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + + if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + + fn = pool_.jobs_.front(); + pool_.jobs_.pop_front(); + } + + assert(true == static_cast(fn)); + fn(); + } + } + + ThreadPool &pool_; + }; + friend struct worker; + + std::vector threads_; + std::list> jobs_; + + bool shutdown_; + size_t max_queued_requests_ = 0; + + std::condition_variable cond_; + std::mutex mutex_; +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +void default_socket_options(socket_t sock); + +const char *status_message(int status); + +std::string get_bearer_token_auth(const Request &req); + +namespace detail { + +class MatcherBase { +public: + virtual ~MatcherBase() = default; + + // Match request path and populate its matches and + virtual bool match(Request &request) const = 0; +}; + +/** + * Captures parameters in request path and stores them in Request::path_params + * + * Capture name is a substring of a pattern from : to /. + * The rest of the pattern is matched agains the request path directly + * Parameters are captured starting from the next character after + * the end of the last matched static pattern fragment until the next /. + * + * Example pattern: + * "/path/fragments/:capture/more/fragments/:second_capture" + * Static fragments: + * "/path/fragments/", "more/fragments/" + * + * Given the following request path: + * "/path/fragments/:1/more/fragments/:2" + * the resulting capture will be + * {{"capture", "1"}, {"second_capture", "2"}} + */ +class PathParamsMatcher final : public MatcherBase { +public: + PathParamsMatcher(const std::string &pattern); + + bool match(Request &request) const override; + +private: + static constexpr char marker = ':'; + // Treat segment separators as the end of path parameter capture + // Does not need to handle query parameters as they are parsed before path + // matching + static constexpr char separator = '/'; + + // Contains static path fragments to match against, excluding the '/' after + // path params + // Fragments are separated by path params + std::vector static_fragments_; + // Stores the names of the path parameters to be used as keys in the + // Request::path_params map + std::vector param_names_; +}; + +/** + * Performs std::regex_match on request path + * and stores the result in Request::matches + * + * Note that regex match is performed directly on the whole request. + * This means that wildcard patterns may match multiple path segments with /: + * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". + */ +class RegexMatcher final : public MatcherBase { +public: + RegexMatcher(const std::string &pattern) : regex_(pattern) {} + + bool match(Request &request) const override; + +private: + std::regex regex_; +}; + +ssize_t write_headers(Stream &strm, const Headers &headers); + +} // namespace detail + +class Server { +public: + using Handler = std::function; + + using ExceptionHandler = + std::function; + + enum class HandlerResponse { + Handled, + Unhandled, + }; + using HandlerWithResponse = + std::function; + + using HandlerWithContentReader = std::function; + + using Expect100ContinueHandler = + std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, HandlerWithContentReader handler); + Server &Put(const std::string &pattern, Handler handler); + Server &Put(const std::string &pattern, HandlerWithContentReader handler); + Server &Patch(const std::string &pattern, Handler handler); + Server &Patch(const std::string &pattern, HandlerWithContentReader handler); + Server &Delete(const std::string &pattern, Handler handler); + Server &Delete(const std::string &pattern, HandlerWithContentReader handler); + Server &Options(const std::string &pattern, Handler handler); + + bool set_base_dir(const std::string &dir, + const std::string &mount_point = std::string()); + bool set_mount_point(const std::string &mount_point, const std::string &dir, + Headers headers = Headers()); + bool remove_mount_point(const std::string &mount_point); + Server &set_file_extension_and_mimetype_mapping(const std::string &ext, + const std::string &mime); + Server &set_default_file_mimetype(const std::string &mime); + Server &set_file_request_handler(Handler handler); + + template + Server &set_error_handler(ErrorHandlerFunc &&handler) { + return set_error_handler_core( + std::forward(handler), + std::is_convertible{}); + } + + Server &set_exception_handler(ExceptionHandler handler); + Server &set_pre_routing_handler(HandlerWithResponse handler); + Server &set_post_routing_handler(Handler handler); + + Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); + Server &set_logger(Logger logger); + + Server &set_address_family(int family); + Server &set_tcp_nodelay(bool on); + Server &set_socket_options(SocketOptions socket_options); + + Server &set_default_headers(Headers headers); + Server & + set_header_writer(std::function const &writer); + + Server &set_keep_alive_max_count(size_t count); + Server &set_keep_alive_timeout(time_t sec); + + Server &set_read_timeout(time_t sec, time_t usec = 0); + template + Server &set_read_timeout(const std::chrono::duration &duration); + + Server &set_write_timeout(time_t sec, time_t usec = 0); + template + Server &set_write_timeout(const std::chrono::duration &duration); + + Server &set_idle_interval(time_t sec, time_t usec = 0); + template + Server &set_idle_interval(const std::chrono::duration &duration); + + Server &set_payload_max_length(size_t length); + + bool bind_to_port(const std::string &host, int port, int socket_flags = 0); + int bind_to_any_port(const std::string &host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const std::string &host, int port, int socket_flags = 0); + + bool is_running() const; + void wait_until_ready() const; + void stop(); + + std::function new_task_queue; + +protected: + bool process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + std::atomic svr_sock_{INVALID_SOCKET}; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + +private: + using Handlers = + std::vector, Handler>>; + using HandlersForContentReader = + std::vector, + HandlerWithContentReader>>; + + static std::unique_ptr + make_matcher(const std::string &pattern); + + Server &set_error_handler_core(HandlerWithResponse handler, std::true_type); + Server &set_error_handler_core(Handler handler, std::false_type); + + socket_t create_server_socket(const std::string &host, int port, + int socket_flags, + SocketOptions socket_options) const; + int bind_internal(const std::string &host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(const Request &req, Response &res, + bool head = false); + bool dispatch_request(Request &req, Response &res, + const Handlers &handlers) const; + bool dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) const; + + bool parse_request_line(const char *s, Request &req) const; + void apply_ranges(const Request &req, Response &res, + std::string &content_type, std::string &boundary) const; + bool write_response(Stream &strm, bool close_connection, Request &req, + Response &res); + bool write_response_with_content(Stream &strm, bool close_connection, + const Request &req, Response &res); + bool write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges); + bool write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool + read_content_with_content_receiver(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) const; + + virtual bool process_and_close_socket(socket_t sock); + + std::atomic is_running_{false}; + std::atomic done_{false}; + + struct MountPointEntry { + std::string mount_point; + std::string base_dir; + Headers headers; + }; + std::vector base_dirs_; + std::map file_extension_and_mimetype_map_; + std::string default_file_mimetype_ = "application/octet-stream"; + Handler file_request_handler_; + + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + + HandlerWithResponse error_handler_; + ExceptionHandler exception_handler_; + HandlerWithResponse pre_routing_handler_; + Handler post_routing_handler_; + Expect100ContinueHandler expect_100_continue_handler_; + + Logger logger_; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; + + Headers default_headers_; + std::function header_writer_ = + detail::write_headers; +}; + +enum class Error { + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification, + UnsupportedMultipartBoundaryChars, + Compression, + ConnectionTimeout, + ProxyConnection, + + // For internal use only + SSLPeerCouldBeClosed_, +}; + +std::string to_string(Error error); + +std::ostream &operator<<(std::ostream &os, const Error &obj); + +class Result { +public: + Result() = default; + Result(std::unique_ptr &&res, Error err, + Headers &&request_headers = Headers{}) + : res_(std::move(res)), err_(err), + request_headers_(std::move(request_headers)) {} + // Response + operator bool() const { return res_ != nullptr; } + bool operator==(std::nullptr_t) const { return res_ == nullptr; } + bool operator!=(std::nullptr_t) const { return res_ != nullptr; } + const Response &value() const { return *res_; } + Response &value() { return *res_; } + const Response &operator*() const { return *res_; } + Response &operator*() { return *res_; } + const Response *operator->() const { return res_.get(); } + Response *operator->() { return res_.get(); } + + // Error + Error error() const { return err_; } + + // Request Headers + bool has_request_header(const std::string &key) const; + std::string get_request_header_value(const std::string &key, + size_t id = 0) const; + uint64_t get_request_header_value_u64(const std::string &key, + size_t id = 0) const; + size_t get_request_header_value_count(const std::string &key) const; + +private: + std::unique_ptr res_; + Error err_ = Error::Unknown; + Headers request_headers_; +}; + +class ClientImpl { +public: + explicit ClientImpl(const std::string &host); + + explicit ClientImpl(const std::string &host, int port); + + explicit ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~ClientImpl(); + + virtual bool is_valid() const; + + Result Get(const std::string &path); + Result Get(const std::string &path, const Headers &headers); + Result Get(const std::string &path, Progress progress); + Result Get(const std::string &path, const Headers &headers, + Progress progress); + Result Get(const std::string &path, ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const std::string &path, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ContentReceiver content_receiver, + Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress = nullptr); + + Result Head(const std::string &path); + Result Head(const std::string &path, const Headers &headers); + + Result Post(const std::string &path); + Result Post(const std::string &path, const Headers &headers); + Result Post(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Post(const std::string &path, const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Put(const std::string &path); + Result Put(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, size_t content_length, + ContentProvider content_provider, const std::string &content_type); + Result Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Put(const std::string &path, const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Patch(const std::string &path); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + + Result Delete(const std::string &path); + Result Delete(const std::string &path, const Headers &headers); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + + Result Options(const std::string &path); + Result Options(const std::string &path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + void stop(); + + std::string host() const; + int port() const; + + size_t is_socket_open() const; + socket_t socket() const; + + void set_hostname_addr_map(std::map addr_map); + + void set_default_headers(Headers headers); + + void + set_header_writer(std::function const &writer); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const std::string &username, const std::string &password); + void set_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const std::string &username, + const std::string &password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const std::string &intf); + + void set_proxy(const std::string &host, int port); + void set_proxy_basic_auth(const std::string &username, + const std::string &password); + void set_proxy_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const std::string &username, + const std::string &password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path = std::string()); + void set_ca_cert_store(X509_STORE *ca_cert_store); + X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + +protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { return sock != INVALID_SOCKET; } + }; + + virtual bool create_and_connect_socket(Socket &socket, Error &error); + + // All of: + // shutdown_ssl + // shutdown_socket + // close_socket + // should ONLY be called when socket_mutex_ is locked. + // Also, shutdown_ssl and close_socket should also NOT be called concurrently + // with a DIFFERENT thread sending requests using that socket. + virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); + void shutdown_socket(Socket &socket) const; + void close_socket(Socket &socket); + + bool process_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + + bool write_content_with_provider(Stream &strm, const Request &req, + Error &error) const; + + void copy_settings(const ClientImpl &rhs); + + // Socket endpoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // These are all protected under socket_mutex + size_t socket_requests_in_flight_ = 0; + std::thread::id socket_requests_are_from_thread_ = std::thread::id(); + bool socket_should_be_closed_when_request_is_done_ = false; + + // Hostname-IP map + std::map addr_map_; + + // Default headers + Headers default_headers_; + + // Header writer + std::function header_writer_ = + detail::write_headers; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; + std::string bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool url_encode_ = true; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = -1; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + std::string proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + + X509_STORE *ca_cert_store_ = nullptr; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool server_certificate_verification_ = true; +#endif + + Logger logger_; + +private: + bool send_(Request &req, Response &res, Error &error); + Result send_(Request &&req); + + socket_t create_client_socket(Error &error) const; + bool read_response_line(Stream &strm, const Request &req, + Response &res) const; + bool write_request(Stream &strm, Request &req, bool close_connection, + Error &error); + bool redirect(Request &req, Response &res, Error &error); + bool handle_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + std::unique_ptr send_with_content_provider( + Request &req, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Error &error); + Result send_with_content_provider( + const std::string &method, const std::string &path, + const Headers &headers, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Progress progress); + ContentProviderWithoutLength get_multipart_content_provider( + const std::string &boundary, const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) const; + + std::string adjust_host_string(const std::string &host) const; + + virtual bool process_socket(const Socket &socket, + std::function callback); + virtual bool is_ssl() const; +}; + +class Client { +public: + // Universal interface + explicit Client(const std::string &scheme_host_port); + + explicit Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path); + + // HTTP only interface + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + Client(Client &&) = default; + + ~Client(); + + bool is_valid() const; + + Result Get(const std::string &path); + Result Get(const std::string &path, const Headers &headers); + Result Get(const std::string &path, Progress progress); + Result Get(const std::string &path, const Headers &headers, + Progress progress); + Result Get(const std::string &path, ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const std::string &path, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ContentReceiver content_receiver, + Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress = nullptr); + + Result Head(const std::string &path); + Result Head(const std::string &path, const Headers &headers); + + Result Post(const std::string &path); + Result Post(const std::string &path, const Headers &headers); + Result Post(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Post(const std::string &path, const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Put(const std::string &path); + Result Put(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, size_t content_length, + ContentProvider content_provider, const std::string &content_type); + Result Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Put(const std::string &path, const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Patch(const std::string &path); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + + Result Delete(const std::string &path); + Result Delete(const std::string &path, const Headers &headers); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + + Result Options(const std::string &path); + Result Options(const std::string &path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + void stop(); + + std::string host() const; + int port() const; + + size_t is_socket_open() const; + socket_t socket() const; + + void set_hostname_addr_map(std::map addr_map); + + void set_default_headers(Headers headers); + + void + set_header_writer(std::function const &writer); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const std::string &username, const std::string &password); + void set_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const std::string &username, + const std::string &password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const std::string &intf); + + void set_proxy(const std::string &host, int port); + void set_proxy_basic_auth(const std::string &username, + const std::string &password); + void set_proxy_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const std::string &username, + const std::string &password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path = std::string()); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; +#endif + +private: + std::unique_ptr cli_; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLServer : public Server { +public: + SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path = nullptr, + const char *client_ca_cert_dir_path = nullptr, + const char *private_key_password = nullptr); + + SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); + + SSLServer( + const std::function &setup_ssl_ctx_callback); + + ~SSLServer() override; + + bool is_valid() const override; + + SSL_CTX *ssl_context() const; + +private: + bool process_and_close_socket(socket_t sock) override; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient final : public ClientImpl { +public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path, + const std::string &private_key_password = std::string()); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, + EVP_PKEY *client_key, + const std::string &private_key_password = std::string()); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + +private: + bool create_and_connect_socket(Socket &socket, Error &error) override; + void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; + void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully); + + bool process_socket(const Socket &socket, + std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success, + Error &error); + bool initialize_ssl(Socket &socket, Error &error); + + bool load_certs(); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::once_flag initialize_cert_; + + std::vector host_components_; + + long verify_result_ = 0; + + friend class ClientImpl; +}; +#endif + +/* + * Implementation of template methods. + */ + +namespace detail { + +template +inline void duration_to_sec_and_usec(const T &duration, U callback) { + auto sec = std::chrono::duration_cast(duration).count(); + auto usec = std::chrono::duration_cast( + duration - std::chrono::seconds(sec)) + .count(); + callback(static_cast(sec), static_cast(usec)); +} + +inline uint64_t get_header_value_u64(const Headers &headers, + const std::string &key, size_t id, + uint64_t def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +} // namespace detail + +inline uint64_t Request::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); +} + +inline uint64_t Response::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { + const auto bufsiz = 2048; + std::array buf{}; + + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); + if (sn <= 0) { return sn; } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&yes), sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&yes), sizeof(yes)); +#endif +#endif +} + +inline const char *status_message(int status) { + switch (status) { + case StatusCode::Continue_100: return "Continue"; + case StatusCode::SwitchingProtocol_101: return "Switching Protocol"; + case StatusCode::Processing_102: return "Processing"; + case StatusCode::EarlyHints_103: return "Early Hints"; + case StatusCode::OK_200: return "OK"; + case StatusCode::Created_201: return "Created"; + case StatusCode::Accepted_202: return "Accepted"; + case StatusCode::NonAuthoritativeInformation_203: + return "Non-Authoritative Information"; + case StatusCode::NoContent_204: return "No Content"; + case StatusCode::ResetContent_205: return "Reset Content"; + case StatusCode::PartialContent_206: return "Partial Content"; + case StatusCode::MultiStatus_207: return "Multi-Status"; + case StatusCode::AlreadyReported_208: return "Already Reported"; + case StatusCode::IMUsed_226: return "IM Used"; + case StatusCode::MultipleChoices_300: return "Multiple Choices"; + case StatusCode::MovedPermanently_301: return "Moved Permanently"; + case StatusCode::Found_302: return "Found"; + case StatusCode::SeeOther_303: return "See Other"; + case StatusCode::NotModified_304: return "Not Modified"; + case StatusCode::UseProxy_305: return "Use Proxy"; + case StatusCode::unused_306: return "unused"; + case StatusCode::TemporaryRedirect_307: return "Temporary Redirect"; + case StatusCode::PermanentRedirect_308: return "Permanent Redirect"; + case StatusCode::BadRequest_400: return "Bad Request"; + case StatusCode::Unauthorized_401: return "Unauthorized"; + case StatusCode::PaymentRequired_402: return "Payment Required"; + case StatusCode::Forbidden_403: return "Forbidden"; + case StatusCode::NotFound_404: return "Not Found"; + case StatusCode::MethodNotAllowed_405: return "Method Not Allowed"; + case StatusCode::NotAcceptable_406: return "Not Acceptable"; + case StatusCode::ProxyAuthenticationRequired_407: + return "Proxy Authentication Required"; + case StatusCode::RequestTimeout_408: return "Request Timeout"; + case StatusCode::Conflict_409: return "Conflict"; + case StatusCode::Gone_410: return "Gone"; + case StatusCode::LengthRequired_411: return "Length Required"; + case StatusCode::PreconditionFailed_412: return "Precondition Failed"; + case StatusCode::PayloadTooLarge_413: return "Payload Too Large"; + case StatusCode::UriTooLong_414: return "URI Too Long"; + case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type"; + case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable"; + case StatusCode::ExpectationFailed_417: return "Expectation Failed"; + case StatusCode::ImATeapot_418: return "I'm a teapot"; + case StatusCode::MisdirectedRequest_421: return "Misdirected Request"; + case StatusCode::UnprocessableContent_422: return "Unprocessable Content"; + case StatusCode::Locked_423: return "Locked"; + case StatusCode::FailedDependency_424: return "Failed Dependency"; + case StatusCode::TooEarly_425: return "Too Early"; + case StatusCode::UpgradeRequired_426: return "Upgrade Required"; + case StatusCode::PreconditionRequired_428: return "Precondition Required"; + case StatusCode::TooManyRequests_429: return "Too Many Requests"; + case StatusCode::RequestHeaderFieldsTooLarge_431: + return "Request Header Fields Too Large"; + case StatusCode::UnavailableForLegalReasons_451: + return "Unavailable For Legal Reasons"; + case StatusCode::NotImplemented_501: return "Not Implemented"; + case StatusCode::BadGateway_502: return "Bad Gateway"; + case StatusCode::ServiceUnavailable_503: return "Service Unavailable"; + case StatusCode::GatewayTimeout_504: return "Gateway Timeout"; + case StatusCode::HttpVersionNotSupported_505: + return "HTTP Version Not Supported"; + case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates"; + case StatusCode::InsufficientStorage_507: return "Insufficient Storage"; + case StatusCode::LoopDetected_508: return "Loop Detected"; + case StatusCode::NotExtended_510: return "Not Extended"; + case StatusCode::NetworkAuthenticationRequired_511: + return "Network Authentication Required"; + + default: + case StatusCode::InternalServerError_500: return "Internal Server Error"; + } +} + +inline std::string get_bearer_token_auth(const Request &req) { + if (req.has_header("Authorization")) { + static std::string BearerHeaderPrefix = "Bearer "; + return req.get_header_value("Authorization") + .substr(BearerHeaderPrefix.length()); + } + return ""; +} + +template +inline Server & +Server::set_read_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_write_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_idle_interval(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); + return *this; +} + +inline std::string to_string(const Error error) { + switch (error) { + case Error::Success: return "Success (no error)"; + case Error::Connection: return "Could not establish connection"; + case Error::BindIPAddress: return "Failed to bind IP address"; + case Error::Read: return "Failed to read connection"; + case Error::Write: return "Failed to write connection"; + case Error::ExceedRedirectCount: return "Maximum redirect count exceeded"; + case Error::Canceled: return "Connection handling canceled"; + case Error::SSLConnection: return "SSL connection failed"; + case Error::SSLLoadingCerts: return "SSL certificate loading failed"; + case Error::SSLServerVerification: return "SSL server verification failed"; + case Error::UnsupportedMultipartBoundaryChars: + return "Unsupported HTTP multipart boundary characters"; + case Error::Compression: return "Compression failed"; + case Error::ConnectionTimeout: return "Connection timed out"; + case Error::ProxyConnection: return "Proxy connection failed"; + case Error::Unknown: return "Unknown"; + default: break; + } + + return "Invalid"; +} + +inline std::ostream &operator<<(std::ostream &os, const Error &obj) { + os << to_string(obj); + os << " (" << static_cast::type>(obj) << ')'; + return os; +} + +inline uint64_t Result::get_request_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(request_headers_, key, id, 0); +} + +template +inline void ClientImpl::set_connection_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { + set_connection_timeout(sec, usec); + }); +} + +template +inline void ClientImpl::set_read_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); +} + +template +inline void ClientImpl::set_write_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); +} + +template +inline void Client::set_connection_timeout( + const std::chrono::duration &duration) { + cli_->set_connection_timeout(duration); +} + +template +inline void +Client::set_read_timeout(const std::chrono::duration &duration) { + cli_->set_read_timeout(duration); +} + +template +inline void +Client::set_write_timeout(const std::chrono::duration &duration) { + cli_->set_write_timeout(duration); +} + +/* + * Forward declarations and types that will be part of the .h file if split into + * .h + .cc. + */ + +std::string hosted_at(const std::string &hostname); + +void hosted_at(const std::string &hostname, std::vector &addrs); + +std::string append_query_params(const std::string &path, const Params ¶ms); + +std::pair make_range_header(const Ranges &ranges); + +std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false); + +namespace detail { + +std::string encode_query_param(const std::string &value); + +std::string decode_url(const std::string &s, bool convert_plus_to_space); + +void read_file(const std::string &path, std::string &out); + +std::string trim_copy(const std::string &s); + +void divide( + const char *data, std::size_t size, char d, + std::function + fn); + +void divide( + const std::string &str, char d, + std::function + fn); + +void split(const char *b, const char *e, char d, + std::function fn); + +void split(const char *b, const char *e, char d, size_t m, + std::function fn); + +bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback); + +socket_t create_client_socket( + const std::string &host, const std::string &ip, int port, + int address_family, bool tcp_nodelay, SocketOptions socket_options, + time_t connection_timeout_sec, time_t connection_timeout_usec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error); + +const char *get_header_value(const Headers &headers, const std::string &key, + size_t id = 0, const char *def = nullptr); + +std::string params_to_query_str(const Params ¶ms); + +void parse_query_text(const char *data, std::size_t size, Params ¶ms); + +void parse_query_text(const std::string &s, Params ¶ms); + +bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary); + +bool parse_range_header(const std::string &s, Ranges &ranges); + +int close_socket(socket_t sock); + +ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags); + +ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags); + +enum class EncodingType { None = 0, Gzip, Brotli }; + +EncodingType encoding_type(const Request &req, const Response &res); + +class BufferStream final : public Stream { +public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + + const std::string &get_buffer() const; + +private: + std::string buffer; + size_t position = 0; +}; + +class compressor { +public: + virtual ~compressor() = default; + + typedef std::function Callback; + virtual bool compress(const char *data, size_t data_length, bool last, + Callback callback) = 0; +}; + +class decompressor { +public: + virtual ~decompressor() = default; + + virtual bool is_valid() const = 0; + + typedef std::function Callback; + virtual bool decompress(const char *data, size_t data_length, + Callback callback) = 0; +}; + +class nocompressor final : public compressor { +public: + ~nocompressor() override = default; + + bool compress(const char *data, size_t data_length, bool /*last*/, + Callback callback) override; +}; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +class gzip_compressor final : public compressor { +public: + gzip_compressor(); + ~gzip_compressor() override; + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; + +class gzip_decompressor final : public decompressor { +public: + gzip_decompressor(); + ~gzip_decompressor() override; + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +class brotli_compressor final : public compressor { +public: + brotli_compressor(); + ~brotli_compressor(); + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + BrotliEncoderState *state_ = nullptr; +}; + +class brotli_decompressor final : public decompressor { +public: + brotli_decompressor(); + ~brotli_decompressor(); + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + BrotliDecoderResult decoder_r; + BrotliDecoderState *decoder_s = nullptr; +}; +#endif + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size); + const char *ptr() const; + size_t size() const; + bool end_with_crlf() const; + bool getline(); + +private: + void append(char c); + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +class mmap { +public: + mmap(const char *path); + ~mmap(); + + bool open(const char *path); + void close(); + + bool is_open() const; + size_t size() const; + const char *data() const; + +private: +#if defined(_WIN32) + HANDLE hFile_; + HANDLE hMapping_; +#else + int fd_; +#endif + size_t size_; + void *addr_; +}; + +} // namespace detail + +// ---------------------------------------------------------------------------- + +/* + * Implementation that will be part of the .cc file if split into .h + .cc. + */ + +namespace detail { + +inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, + int &val) { + if (i >= s.size()) { return false; } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { return false; } + auto v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline std::string from_i_to_hex(size_t n) { + static const auto charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; +} + +inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = static_cast(code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +inline std::string base64_encode(const std::string &in) { + static const auto lookup = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + std::string out; + out.reserve(in.size()); + + auto val = 0; + auto valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; +} + +inline bool is_file(const std::string &path) { +#ifdef _WIN32 + return _access_s(path.c_str(), 0) == 0; +#else + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +#endif +} + +inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + if (path[i] == '\0') { + return false; + } else if (path[i] == '\\') { + return false; + } + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { return false; } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline std::string encode_query_param(const std::string &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (auto c : value) { + if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || + c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || + c == ')') { + escaped << c; + } else { + escaped << std::uppercase; + escaped << '%' << std::setw(2) + << static_cast(static_cast(c)); + escaped << std::nouppercase; + } + } + + return escaped.str(); +} + +inline std::string encode_url(const std::string &s) { + std::string result; + result.reserve(s.size()); + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "%20"; break; + case '+': result += "%2B"; break; + case '\r': result += "%0D"; break; + case '\n': result += "%0A"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': result += "%3B"; break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s, + bool convert_plus_to_space) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + auto val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { result.append(buff, len); } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + auto val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); +} + +inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { return m[1].str(); } + return std::string(); +} + +inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } + +inline std::pair trim(const char *b, const char *e, size_t left, + size_t right) { + while (b + left < e && is_space_or_tab(b[left])) { + left++; + } + while (right > 0 && is_space_or_tab(b[right - 1])) { + right--; + } + return std::make_pair(left, right); +} + +inline std::string trim_copy(const std::string &s) { + auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); + return s.substr(r.first, r.second - r.first); +} + +inline std::string trim_double_quotes_copy(const std::string &s) { + if (s.length() >= 2 && s.front() == '"' && s.back() == '"') { + return s.substr(1, s.size() - 2); + } + return s; +} + +inline void +divide(const char *data, std::size_t size, char d, + std::function + fn) { + const auto it = std::find(data, data + size, d); + const auto found = static_cast(it != data + size); + const auto lhs_data = data; + const auto lhs_size = static_cast(it - data); + const auto rhs_data = it + found; + const auto rhs_size = size - lhs_size - found; + + fn(lhs_data, lhs_size, rhs_data, rhs_size); +} + +inline void +divide(const std::string &str, char d, + std::function + fn) { + divide(str.data(), str.size(), d, std::move(fn)); +} + +inline void split(const char *b, const char *e, char d, + std::function fn) { + return split(b, e, d, (std::numeric_limits::max)(), std::move(fn)); +} + +inline void split(const char *b, const char *e, char d, size_t m, + std::function fn) { + size_t i = 0; + size_t beg = 0; + size_t count = 1; + + while (e ? (b + i < e) : (b[i] != '\0')) { + if (b[i] == d && count < m) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + beg = i + 1; + count++; + } + i++; + } + + if (i) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + } +} + +inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} + +inline const char *stream_line_reader::ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } +} + +inline size_t stream_line_reader::size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } +} + +inline bool stream_line_reader::end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; +} + +inline bool stream_line_reader::getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { break; } + } + + return true; +} + +inline void stream_line_reader::append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } +} + +inline mmap::mmap(const char *path) +#if defined(_WIN32) + : hFile_(NULL), hMapping_(NULL) +#else + : fd_(-1) +#endif + , + size_(0), addr_(nullptr) { + open(path); +} + +inline mmap::~mmap() { close(); } + +inline bool mmap::open(const char *path) { + close(); + +#if defined(_WIN32) + std::wstring wpath; + for (size_t i = 0; i < strlen(path); i++) { + wpath += path[i]; + } + + hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, NULL); + + if (hFile_ == INVALID_HANDLE_VALUE) { return false; } + + LARGE_INTEGER size{}; + if (!::GetFileSizeEx(hFile_, &size)) { return false; } + size_ = static_cast(size.QuadPart); + + hMapping_ = + ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL); + + if (hMapping_ == NULL) { + close(); + return false; + } + + addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0); +#else + fd_ = ::open(path, O_RDONLY); + if (fd_ == -1) { return false; } + + struct stat sb; + if (fstat(fd_, &sb) == -1) { + close(); + return false; + } + size_ = static_cast(sb.st_size); + + addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0); +#endif + + if (addr_ == nullptr) { + close(); + return false; + } + + return true; +} + +inline bool mmap::is_open() const { return addr_ != nullptr; } + +inline size_t mmap::size() const { return size_; } + +inline const char *mmap::data() const { + return static_cast(addr_); +} + +inline void mmap::close() { +#if defined(_WIN32) + if (addr_) { + ::UnmapViewOfFile(addr_); + addr_ = nullptr; + } + + if (hMapping_) { + ::CloseHandle(hMapping_); + hMapping_ = NULL; + } + + if (hFile_ != INVALID_HANDLE_VALUE) { + ::CloseHandle(hFile_); + hFile_ = INVALID_HANDLE_VALUE; + } +#else + if (addr_ != nullptr) { + munmap(addr_, size_); + addr_ = nullptr; + } + + if (fd_ != -1) { + ::close(fd_); + fd_ = -1; + } +#endif + size_ = 0; +} +inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +template inline ssize_t handle_EINTR(T fn) { + ssize_t res = 0; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { continue; } + break; + } + return res; +} + +inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { + return handle_EINTR([&]() { + return recv(sock, +#ifdef _WIN32 + static_cast(ptr), static_cast(size), +#else + ptr, size, +#endif + flags); + }); +} + +inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, + int flags) { + return handle_EINTR([&]() { + return send(sock, +#ifdef _WIN32 + static_cast(ptr), static_cast(size), +#else + ptr, size, +#endif + flags); + }); +} + +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return -1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + }); +#endif +} + +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return -1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); + }); +#endif +} + +inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, + time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res == 0) { return Error::ConnectionTimeout; } + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + auto error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + auto successful = res >= 0 && !error; + return successful ? Error::Success : Error::Connection; + } + + return Error::Connection; +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return Error::Connection; } +#endif + + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { + return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); + }); + + if (ret == 0) { return Error::ConnectionTimeout; } + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + auto error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + auto successful = res >= 0 && !error; + return successful ? Error::Success : Error::Connection; + } + return Error::Connection; +#endif +} + +inline bool is_socket_alive(socket_t sock) { + const auto val = detail::select_read(sock, 0, 0); + if (val == 0) { + return true; + } else if (val < 0 && errno == EBADF) { + return false; + } + char buf[1]; + return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0; +} + +class SocketStream final : public Stream { +public: + SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; + + std::vector read_buff_; + size_t read_buff_off_ = 0; + size_t read_buff_content_size_ = 0; + + static const size_t read_buff_size_ = 1024l * 4; +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream final : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + +inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = keep_alive_timeout_sec * 1000; + if (duration.count() > timeout) { return false; } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } +} + +template +inline bool +process_server_socket_core(const std::atomic &svr_sock, socket_t sock, + size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (svr_sock != INVALID_SOCKET && count > 0 && + keep_alive(sock, keep_alive_timeout_sec)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { break; } + count--; + } + return ret; +} + +template +inline bool +process_server_socket(const std::atomic &svr_sock, socket_t sock, + size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const std::string &host, const std::string &ip, int port, + int address_family, int socket_flags, bool tcp_nodelay, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + const char *node = nullptr; + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (!ip.empty()) { + node = ip.c_str(); + // Ask getaddrinfo to convert IP in c-string to address + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + } else { + if (!host.empty()) { node = host.c_str(); } + hints.ai_family = address_family; + hints.ai_flags = socket_flags; + } + +#ifndef _WIN32 + if (hints.ai_family == AF_UNIX) { + const auto addrlen = host.length(); + if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; } + + auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); + if (sock != INVALID_SOCKET) { + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + std::copy(host.begin(), host.end(), addr.sun_path); + + hints.ai_addr = reinterpret_cast(&addr); + hints.ai_addrlen = static_cast( + sizeof(addr) - sizeof(addr.sun_path) + addrlen); + + fcntl(sock, F_SETFD, FD_CLOEXEC); + if (socket_options) { socket_options(sock); } + + if (!bind_or_connect(sock, hints)) { + close_socket(sock); + sock = INVALID_SOCKET; + } + } + return sock; + } +#endif + + auto service = std::to_string(port); + + if (getaddrinfo(node, service.c_str(), &hints, &result)) { +#if defined __linux__ && !defined __ANDROID__ + res_init(); +#endif + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = + WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, + WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { continue; } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { + close_socket(sock); + continue; + } +#endif + + if (tcp_nodelay) { + auto yes = 1; +#ifdef _WIN32 + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast(&yes), sizeof(yes)); +#endif + } + + if (socket_options) { socket_options(sock); } + + if (rp->ai_family == AF_INET6) { + auto no = 0; +#ifdef _WIN32 + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&no), sizeof(no)); +#else + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&no), sizeof(no)); +#endif + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline bool bind_ip_address(socket_t sock, const std::string &host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; +} + +#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__ +#define USE_IF2IP +#endif + +#ifdef USE_IF2IP +inline std::string if2ip(int address_family, const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + std::string addr_candidate; + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name && + (AF_UNSPEC == address_family || + ifa->ifa_addr->sa_family == address_family)) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + auto sa = reinterpret_cast(ifa->ifa_addr); + if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) { + char buf[INET6_ADDRSTRLEN] = {}; + if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) { + // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL + auto s6_addr_head = sa->sin6_addr.s6_addr[0]; + if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) { + addr_candidate = std::string(buf, INET6_ADDRSTRLEN); + } else { + freeifaddrs(ifap); + return std::string(buf, INET6_ADDRSTRLEN); + } + } + } + } + } + } + freeifaddrs(ifap); + return addr_candidate; +} +#endif + +inline socket_t create_client_socket( + const std::string &host, const std::string &ip, int port, + int address_family, bool tcp_nodelay, SocketOptions socket_options, + time_t connection_timeout_sec, time_t connection_timeout_usec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error) { + auto sock = create_socket( + host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options), + [&](socket_t sock2, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifdef USE_IF2IP + auto ip_from_if = if2ip(address_family, intf); + if (ip_from_if.empty()) { ip_from_if = intf; } + if (!bind_ip_address(sock2, ip_from_if)) { + error = Error::BindIPAddress; + return false; + } +#endif + } + + set_nonblocking(sock2, true); + + auto ret = + ::connect(sock2, ai.ai_addr, static_cast(ai.ai_addrlen)); + + if (ret < 0) { + if (is_connection_error()) { + error = Error::Connection; + return false; + } + error = wait_until_socket_is_ready(sock2, connection_timeout_sec, + connection_timeout_usec); + if (error != Error::Success) { return false; } + } + + set_nonblocking(sock2, false); + + { +#ifdef _WIN32 + auto timeout = static_cast(read_timeout_sec * 1000 + + read_timeout_usec / 1000); + setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec); + tv.tv_usec = static_cast(read_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + { + +#ifdef _WIN32 + auto timeout = static_cast(write_timeout_sec * 1000 + + write_timeout_usec / 1000); + setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec); + tv.tv_usec = static_cast(write_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + + error = Error::Success; + return true; + }); + + if (sock != INVALID_SOCKET) { + error = Error::Success; + } else { + if (error == Error::Success) { error = Error::Connection; } + } + + return sock; +} + +inline bool get_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, std::string &ip, int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = + ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return false; + } + + std::array ipstr{}; + if (getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(ipstr.size()), nullptr, + 0, NI_NUMERICHOST)) { + return false; + } + + ip = ipstr.data(); + return true; +} + +inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (!getsockname(sock, reinterpret_cast(&addr), + &addr_len)) { + get_ip_and_port(addr, addr_len, ip, port); + } +} + +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), + &addr_len)) { +#ifndef _WIN32 + if (addr.ss_family == AF_UNIX) { +#if defined(__linux__) + struct ucred ucred; + socklen_t len = sizeof(ucred); + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) { + port = ucred.pid; + } +#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__ + pid_t pid; + socklen_t len = sizeof(pid); + if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) { + port = pid; + } +#endif + return; + } +#endif + get_ip_and_port(addr, addr_len, ip, port); + } +} + +inline constexpr unsigned int str2tag_core(const char *s, size_t l, + unsigned int h) { + return (l == 0) + ? h + : str2tag_core( + s + 1, l - 1, + // Unsets the 6 high bits of h, therefore no overflow happens + (((std::numeric_limits::max)() >> 6) & + h * 33) ^ + static_cast(*s)); +} + +inline unsigned int str2tag(const std::string &s) { + return str2tag_core(s.data(), s.size(), 0); +} + +namespace udl { + +inline constexpr unsigned int operator"" _t(const char *s, size_t l) { + return str2tag_core(s, l, 0); +} + +} // namespace udl + +inline std::string +find_content_type(const std::string &path, + const std::map &user_data, + const std::string &default_content_type) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { return it->second; } + + using udl::operator""_t; + + switch (str2tag(ext)) { + default: return default_content_type; + + case "css"_t: return "text/css"; + case "csv"_t: return "text/csv"; + case "htm"_t: + case "html"_t: return "text/html"; + case "js"_t: + case "mjs"_t: return "text/javascript"; + case "txt"_t: return "text/plain"; + case "vtt"_t: return "text/vtt"; + + case "apng"_t: return "image/apng"; + case "avif"_t: return "image/avif"; + case "bmp"_t: return "image/bmp"; + case "gif"_t: return "image/gif"; + case "png"_t: return "image/png"; + case "svg"_t: return "image/svg+xml"; + case "webp"_t: return "image/webp"; + case "ico"_t: return "image/x-icon"; + case "tif"_t: return "image/tiff"; + case "tiff"_t: return "image/tiff"; + case "jpg"_t: + case "jpeg"_t: return "image/jpeg"; + + case "mp4"_t: return "video/mp4"; + case "mpeg"_t: return "video/mpeg"; + case "webm"_t: return "video/webm"; + + case "mp3"_t: return "audio/mp3"; + case "mpga"_t: return "audio/mpeg"; + case "weba"_t: return "audio/webm"; + case "wav"_t: return "audio/wave"; + + case "otf"_t: return "font/otf"; + case "ttf"_t: return "font/ttf"; + case "woff"_t: return "font/woff"; + case "woff2"_t: return "font/woff2"; + + case "7z"_t: return "application/x-7z-compressed"; + case "atom"_t: return "application/atom+xml"; + case "pdf"_t: return "application/pdf"; + case "json"_t: return "application/json"; + case "rss"_t: return "application/rss+xml"; + case "tar"_t: return "application/x-tar"; + case "xht"_t: + case "xhtml"_t: return "application/xhtml+xml"; + case "xslt"_t: return "application/xslt+xml"; + case "xml"_t: return "application/xml"; + case "gz"_t: return "application/gzip"; + case "zip"_t: return "application/zip"; + case "wasm"_t: return "application/wasm"; + } +} + +inline bool can_compress_content_type(const std::string &content_type) { + using udl::operator""_t; + + auto tag = str2tag(content_type); + + switch (tag) { + case "image/svg+xml"_t: + case "application/javascript"_t: + case "application/json"_t: + case "application/xml"_t: + case "application/protobuf"_t: + case "application/xhtml+xml"_t: return true; + + default: + return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t; + } +} + +inline EncodingType encoding_type(const Request &req, const Response &res) { + auto ret = + detail::can_compress_content_type(res.get_header_value("Content-Type")); + if (!ret) { return EncodingType::None; } + + const auto &s = req.get_header_value("Accept-Encoding"); + (void)(s); + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + // TODO: 'Accept-Encoding' has br, not br;q=0 + ret = s.find("br") != std::string::npos; + if (ret) { return EncodingType::Brotli; } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + ret = s.find("gzip") != std::string::npos; + if (ret) { return EncodingType::Gzip; } +#endif + + return EncodingType::None; +} + +inline bool nocompressor::compress(const char *data, size_t data_length, + bool /*last*/, Callback callback) { + if (!data_length) { return true; } + return callback(data, data_length); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline gzip_compressor::gzip_compressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY) == Z_OK; +} + +inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); } + +inline bool gzip_compressor::compress(const char *data, size_t data_length, + bool last, Callback callback) { + assert(is_valid_); + + do { + constexpr size_t max_avail_in = + (std::numeric_limits::max)(); + + strm_.avail_in = static_cast( + (std::min)(data_length, max_avail_in)); + strm_.next_in = const_cast(reinterpret_cast(data)); + + data_length -= strm_.avail_in; + data += strm_.avail_in; + + auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH; + auto ret = Z_OK; + + std::array buff{}; + do { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = deflate(&strm_, flush); + if (ret == Z_STREAM_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } while (strm_.avail_out == 0); + + assert((flush == Z_FINISH && ret == Z_STREAM_END) || + (flush == Z_NO_FLUSH && ret == Z_OK)); + assert(strm_.avail_in == 0); + } while (data_length > 0); + + return true; +} + +inline gzip_decompressor::gzip_decompressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; +} + +inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); } + +inline bool gzip_decompressor::is_valid() const { return is_valid_; } + +inline bool gzip_decompressor::decompress(const char *data, size_t data_length, + Callback callback) { + assert(is_valid_); + + auto ret = Z_OK; + + do { + constexpr size_t max_avail_in = + (std::numeric_limits::max)(); + + strm_.avail_in = static_cast( + (std::min)(data_length, max_avail_in)); + strm_.next_in = const_cast(reinterpret_cast(data)); + + data_length -= strm_.avail_in; + data += strm_.avail_in; + + std::array buff{}; + while (strm_.avail_in > 0 && ret == Z_OK) { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm_, Z_NO_FLUSH); + + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: inflateEnd(&strm_); return false; + } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } + + if (ret != Z_OK && ret != Z_STREAM_END) { return false; } + + } while (data_length > 0); + + return true; +} +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +inline brotli_compressor::brotli_compressor() { + state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); +} + +inline brotli_compressor::~brotli_compressor() { + BrotliEncoderDestroyInstance(state_); +} + +inline bool brotli_compressor::compress(const char *data, size_t data_length, + bool last, Callback callback) { + std::array buff{}; + + auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; + auto available_in = data_length; + auto next_in = reinterpret_cast(data); + + for (;;) { + if (last) { + if (BrotliEncoderIsFinished(state_)) { break; } + } else { + if (!available_in) { break; } + } + + auto available_out = buff.size(); + auto next_out = buff.data(); + + if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, + &available_out, &next_out, nullptr)) { + return false; + } + + auto output_bytes = buff.size() - available_out; + if (output_bytes) { + callback(reinterpret_cast(buff.data()), output_bytes); + } + } + + return true; +} + +inline brotli_decompressor::brotli_decompressor() { + decoder_s = BrotliDecoderCreateInstance(0, 0, 0); + decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT + : BROTLI_DECODER_RESULT_ERROR; +} + +inline brotli_decompressor::~brotli_decompressor() { + if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } +} + +inline bool brotli_decompressor::is_valid() const { return decoder_s; } + +inline bool brotli_decompressor::decompress(const char *data, + size_t data_length, + Callback callback) { + if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_ERROR) { + return 0; + } + + auto next_in = reinterpret_cast(data); + size_t avail_in = data_length; + size_t total_out; + + decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + std::array buff{}; + while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + char *next_out = buff.data(); + size_t avail_out = buff.size(); + + decoder_r = BrotliDecoderDecompressStream( + decoder_s, &avail_in, &next_in, &avail_out, + reinterpret_cast(&next_out), &total_out); + + if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - avail_out)) { return false; } + } + + return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; +} +#endif + +inline bool has_header(const Headers &headers, const std::string &key) { + return headers.find(key) != headers.end(); +} + +inline const char *get_header_value(const Headers &headers, + const std::string &key, size_t id, + const char *def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second.c_str(); } + return def; +} + +inline bool compare_case_ignore(const std::string &a, const std::string &b) { + if (a.size() != b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; +} + +template +inline bool parse_header(const char *beg, const char *end, T fn) { + // Skip trailing spaces and tabs. + while (beg < end && is_space_or_tab(end[-1])) { + end--; + } + + auto p = beg; + while (p < end && *p != ':') { + p++; + } + + if (p == end) { return false; } + + auto key_end = p; + + if (*p++ != ':') { return false; } + + while (p < end && is_space_or_tab(*p)) { + p++; + } + + if (p < end) { + auto key_len = key_end - beg; + if (!key_len) { return false; } + + auto key = std::string(beg, key_end); + auto val = compare_case_ignore(key, "Location") + ? std::string(p, end) + : decode_url(std::string(p, end), false); + fn(key, val); + return true; + } + + return false; +} + +inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { return false; } + + // Check if the line ends with CRLF. + auto line_terminator_len = 2; + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { break; } +#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR + } else { + // Blank line indicates end of headers. + if (line_reader.size() == 1) { break; } + line_terminator_len = 1; + } +#else + } else { + continue; // Skip invalid line. + } +#endif + + if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } + + // Exclude line terminator + auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; + + parse_header(line_reader.ptr(), end, + [&](const std::string &key, const std::string &val) { + headers.emplace(key, val); + }); + } + + return true; +} + +inline bool read_content_with_length(Stream &strm, uint64_t len, + Progress progress, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return false; } + + if (!out(buf, static_cast(n), r, len)) { return false; } + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { return false; } + } + } + + return true; +} + +inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return; } + r += static_cast(n); + } +} + +inline bool read_content_without_length(Stream &strm, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n <= 0) { return true; } + + if (!out(buf, static_cast(n), r, 0)) { return false; } + r += static_cast(n); + } + + return true; +} + +template +inline bool read_content_chunked(Stream &strm, T &x, + ContentReceiverWithProgress out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { return false; } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { return false; } + if (chunk_len == ULONG_MAX) { return false; } + + if (chunk_len == 0) { break; } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { return false; } + + if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; } + + if (!line_reader.getline()) { return false; } + } + + assert(chunk_len == 0); + + // Trailer + if (!line_reader.getline()) { return false; } + + while (strcmp(line_reader.ptr(), "\r\n") != 0) { + if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } + + // Exclude line terminator + constexpr auto line_terminator_len = 2; + auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; + + parse_header(line_reader.ptr(), end, + [&](const std::string &key, const std::string &val) { + x.headers.emplace(key, val); + }); + + if (!line_reader.getline()) { return false; } + } + + return true; +} + +inline bool is_chunked_transfer_encoding(const Headers &headers) { + return compare_case_ignore( + get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); +} + +template +bool prepare_content_receiver(T &x, int &status, + ContentReceiverWithProgress receiver, + bool decompress, U callback) { + if (decompress) { + std::string encoding = x.get_header_value("Content-Encoding"); + std::unique_ptr decompressor; + + if (encoding == "gzip" || encoding == "deflate") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor = detail::make_unique(); +#else + status = StatusCode::UnsupportedMediaType_415; + return false; +#endif + } else if (encoding.find("br") != std::string::npos) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + decompressor = detail::make_unique(); +#else + status = StatusCode::UnsupportedMediaType_415; + return false; +#endif + } + + if (decompressor) { + if (decompressor->is_valid()) { + ContentReceiverWithProgress out = [&](const char *buf, size_t n, + uint64_t off, uint64_t len) { + return decompressor->decompress(buf, n, + [&](const char *buf2, size_t n2) { + return receiver(buf2, n2, off, len); + }); + }; + return callback(std::move(out)); + } else { + status = StatusCode::InternalServerError_500; + return false; + } + } + } + + ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, + uint64_t len) { + return receiver(buf, n, off, len); + }; + return callback(std::move(out)); +} + +template +bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, + Progress progress, ContentReceiverWithProgress receiver, + bool decompress) { + return prepare_content_receiver( + x, status, std::move(receiver), decompress, + [&](const ContentReceiverWithProgress &out) { + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, x, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, std::move(progress), out); + } + } + + if (!ret) { + status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 + : StatusCode::BadRequest_400; + } + return ret; + }); +} // namespace detail + +inline ssize_t write_headers(Stream &strm, const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : headers) { + auto len = + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { return len; } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { return len; } + write_len += len; + return write_len; +} + +inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { return false; } + offset += static_cast(length); + } + return true; +} + +template +inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, T is_shutting_down, + Error &error) { + size_t end_offset = offset + length; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + if (strm.is_writable() && write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + + while (offset < end_offset && !is_shutting_down()) { + if (!strm.is_writable()) { + error = Error::Write; + return false; + } else if (!content_provider(offset, end_offset - offset, data_sink)) { + error = Error::Canceled; + return false; + } else if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; +} + +template +inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, + const T &is_shutting_down) { + auto error = Error::Success; + return write_content(strm, content_provider, offset, length, is_shutting_down, + error); +} + +template +inline bool +write_content_without_length(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + offset += l; + if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; } + } + return ok; + }; + + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + + data_sink.done = [&](void) { data_available = false; }; + + while (data_available && !is_shutting_down()) { + if (!strm.is_writable()) { + return false; + } else if (!content_provider(offset, 0, data_sink)) { + return false; + } else if (!ok) { + return false; + } + } + return true; +} + +template +inline bool +write_content_chunked(Stream &strm, const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor, Error &error) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + data_available = l > 0; + offset += l; + + std::string payload; + if (compressor.compress(d, l, false, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = + from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!strm.is_writable() || + !write_data(strm, chunk.data(), chunk.size())) { + ok = false; + } + } + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + + auto done_with_trailer = [&](const Headers *trailer) { + if (!ok) { return; } + + data_available = false; + + std::string payload; + if (!compressor.compress(nullptr, 0, true, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + ok = false; + return; + } + + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!strm.is_writable() || + !write_data(strm, chunk.data(), chunk.size())) { + ok = false; + return; + } + } + + static const std::string done_marker("0\r\n"); + if (!write_data(strm, done_marker.data(), done_marker.size())) { + ok = false; + } + + // Trailer + if (trailer) { + for (const auto &kv : *trailer) { + std::string field_line = kv.first + ": " + kv.second + "\r\n"; + if (!write_data(strm, field_line.data(), field_line.size())) { + ok = false; + } + } + } + + static const std::string crlf("\r\n"); + if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; } + }; + + data_sink.done = [&](void) { done_with_trailer(nullptr); }; + + data_sink.done_with_trailer = [&](const Headers &trailer) { + done_with_trailer(&trailer); + }; + + while (data_available && !is_shutting_down()) { + if (!strm.is_writable()) { + error = Error::Write; + return false; + } else if (!content_provider(offset, 0, data_sink)) { + error = Error::Canceled; + return false; + } else if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; +} + +template +inline bool write_content_chunked(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor) { + auto error = Error::Success; + return write_content_chunked(strm, content_provider, is_shutting_down, + compressor, error); +} + +template +inline bool redirect(T &cli, Request &req, Response &res, + const std::string &path, const std::string &location, + Error &error) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count_ -= 1; + + if (res.status == StatusCode::SeeOther_303 && + (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res, error); + if (ret) { + req = new_req; + res = new_res; + + if (res.location.empty()) { res.location = location; } + } + return ret; +} + +inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += encode_query_param(it->second); + } + return query; +} + +inline void parse_query_text(const char *data, std::size_t size, + Params ¶ms) { + std::set cache; + split(data, data + size, '&', [&](const char *b, const char *e) { + std::string kv(b, e); + if (cache.find(kv) != cache.end()) { return; } + cache.insert(std::move(kv)); + + std::string key; + std::string val; + divide(b, static_cast(e - b), '=', + [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data, + std::size_t rhs_size) { + key.assign(lhs_data, lhs_size); + val.assign(rhs_data, rhs_size); + }); + + if (!key.empty()) { + params.emplace(decode_url(key, true), decode_url(val, true)); + } + }); +} + +inline void parse_query_text(const std::string &s, Params ¶ms) { + parse_query_text(s.data(), s.size(), params); +} + +inline bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary) { + auto boundary_keyword = "boundary="; + auto pos = content_type.find(boundary_keyword); + if (pos == std::string::npos) { return false; } + auto end = content_type.find(';', pos); + auto beg = pos + strlen(boundary_keyword); + boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg)); + return !boundary.empty(); +} + +inline void parse_disposition_params(const std::string &s, Params ¶ms) { + std::set cache; + split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) { + std::string kv(b, e); + if (cache.find(kv) != cache.end()) { return; } + cache.insert(kv); + + std::string key; + std::string val; + split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + + if (!key.empty()) { + params.emplace(trim_double_quotes_copy((key)), + trim_double_quotes_copy((val))); + } + }); +} + +#ifdef CPPHTTPLIB_NO_EXCEPTIONS +inline bool parse_range_header(const std::string &s, Ranges &ranges) { +#else +inline bool parse_range_header(const std::string &s, Ranges &ranges) try { +#endif + auto is_valid = [](const std::string &str) { + return std::all_of(str.cbegin(), str.cend(), + [](unsigned char c) { return std::isdigit(c); }); + }; + + if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) { + const auto pos = static_cast(6); + const auto len = static_cast(s.size() - 6); + auto all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) { return; } + + const auto it = std::find(b, e, '-'); + if (it == e) { + all_valid_ranges = false; + return; + } + + const auto lhs = std::string(b, it); + const auto rhs = std::string(it + 1, e); + if (!is_valid(lhs) || !is_valid(rhs)) { + all_valid_ranges = false; + return; + } + + const auto first = + static_cast(lhs.empty() ? -1 : std::stoll(lhs)); + const auto last = + static_cast(rhs.empty() ? -1 : std::stoll(rhs)); + if ((first == -1 && last == -1) || + (first != -1 && last != -1 && first > last)) { + all_valid_ranges = false; + return; + } + + ranges.emplace_back(first, last); + }); + return all_valid_ranges && !ranges.empty(); + } + return false; +#ifdef CPPHTTPLIB_NO_EXCEPTIONS +} +#else +} catch (...) { return false; } +#endif + +class MultipartFormDataParser { +public: + MultipartFormDataParser() = default; + + void set_boundary(std::string &&boundary) { + boundary_ = boundary; + dash_boundary_crlf_ = dash_ + boundary_ + crlf_; + crlf_dash_boundary_ = crlf_ + dash_ + boundary_; + } + + bool is_valid() const { return is_valid_; } + + bool parse(const char *buf, size_t n, const ContentReceiver &content_callback, + const MultipartContentHeader &header_callback) { + + buf_append(buf, n); + + while (buf_size() > 0) { + switch (state_) { + case 0: { // Initial boundary + buf_erase(buf_find(dash_boundary_crlf_)); + if (dash_boundary_crlf_.size() > buf_size()) { return true; } + if (!buf_start_with(dash_boundary_crlf_)) { return false; } + buf_erase(dash_boundary_crlf_.size()); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_find(crlf_); + if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } + while (pos < buf_size()) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_erase(crlf_.size()); + state_ = 3; + break; + } + + const auto header = buf_head(pos); + + if (!parse_header(header.data(), header.data() + header.size(), + [&](const std::string &, const std::string &) {})) { + is_valid_ = false; + return false; + } + + static const std::string header_content_type = "Content-Type:"; + + if (start_with_case_ignore(header, header_content_type)) { + file_.content_type = + trim_copy(header.substr(header_content_type.size())); + } else { + static const std::regex re_content_disposition( + R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~", + std::regex_constants::icase); + + std::smatch m; + if (std::regex_match(header, m, re_content_disposition)) { + Params params; + parse_disposition_params(m[1], params); + + auto it = params.find("name"); + if (it != params.end()) { + file_.name = it->second; + } else { + is_valid_ = false; + return false; + } + + it = params.find("filename"); + if (it != params.end()) { file_.filename = it->second; } + + it = params.find("filename*"); + if (it != params.end()) { + // Only allow UTF-8 enconnding... + static const std::regex re_rfc5987_encoding( + R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase); + + std::smatch m2; + if (std::regex_match(it->second, m2, re_rfc5987_encoding)) { + file_.filename = decode_url(m2[1], false); // override... + } else { + is_valid_ = false; + return false; + } + } + } + } + buf_erase(pos + crlf_.size()); + pos = buf_find(crlf_); + } + if (state_ != 3) { return true; } + break; + } + case 3: { // Body + if (crlf_dash_boundary_.size() > buf_size()) { return true; } + auto pos = buf_find(crlf_dash_boundary_); + if (pos < buf_size()) { + if (!content_callback(buf_data(), pos)) { + is_valid_ = false; + return false; + } + buf_erase(pos + crlf_dash_boundary_.size()); + state_ = 4; + } else { + auto len = buf_size() - crlf_dash_boundary_.size(); + if (len > 0) { + if (!content_callback(buf_data(), len)) { + is_valid_ = false; + return false; + } + buf_erase(len); + } + return true; + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_size()) { return true; } + if (buf_start_with(crlf_)) { + buf_erase(crlf_.size()); + state_ = 1; + } else { + if (dash_.size() > buf_size()) { return true; } + if (buf_start_with(dash_)) { + buf_erase(dash_.size()); + is_valid_ = true; + buf_erase(buf_size()); // Remove epilogue + } else { + return true; + } + } + break; + } + } + } + + return true; + } + +private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + bool start_with_case_ignore(const std::string &a, + const std::string &b) const { + if (a.size() < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; + } + + const std::string dash_ = "--"; + const std::string crlf_ = "\r\n"; + std::string boundary_; + std::string dash_boundary_crlf_; + std::string crlf_dash_boundary_; + + size_t state_ = 0; + bool is_valid_ = false; + MultipartFormData file_; + + // Buffer + bool start_with(const std::string &a, size_t spos, size_t epos, + const std::string &b) const { + if (epos - spos < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (a[i + spos] != b[i]) { return false; } + } + return true; + } + + size_t buf_size() const { return buf_epos_ - buf_spos_; } + + const char *buf_data() const { return &buf_[buf_spos_]; } + + std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); } + + bool buf_start_with(const std::string &s) const { + return start_with(buf_, buf_spos_, buf_epos_, s); + } + + size_t buf_find(const std::string &s) const { + auto c = s.front(); + + size_t off = buf_spos_; + while (off < buf_epos_) { + auto pos = off; + while (true) { + if (pos == buf_epos_) { return buf_size(); } + if (buf_[pos] == c) { break; } + pos++; + } + + auto remaining_size = buf_epos_ - pos; + if (s.size() > remaining_size) { return buf_size(); } + + if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; } + + off = pos + 1; + } + + return buf_size(); + } + + void buf_append(const char *data, size_t n) { + auto remaining_size = buf_size(); + if (remaining_size > 0 && buf_spos_ > 0) { + for (size_t i = 0; i < remaining_size; i++) { + buf_[i] = buf_[buf_spos_ + i]; + } + } + buf_spos_ = 0; + buf_epos_ = remaining_size; + + if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); } + + for (size_t i = 0; i < n; i++) { + buf_[buf_epos_ + i] = data[i]; + } + buf_epos_ += n; + } + + void buf_erase(size_t size) { buf_spos_ += size; } + + std::string buf_; + size_t buf_spos_ = 0; + size_t buf_epos_ = 0; +}; + +inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; +} + +inline std::string random_string(size_t length) { + static const char data[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + // std::random_device might actually be deterministic on some + // platforms, but due to lack of support in the c++ standard library, + // doing better requires either some ugly hacks or breaking portability. + static std::random_device seed_gen; + + // Request 128 bits of entropy for initialization + static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), + seed_gen()}; + + static std::mt19937 engine(seed_sequence); + + std::string result; + for (size_t i = 0; i < length; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + return result; +} + +inline std::string make_multipart_data_boundary() { + return "--cpp-httplib-multipart-data-" + detail::random_string(16); +} + +inline bool is_multipart_boundary_chars_valid(const std::string &boundary) { + auto valid = true; + for (size_t i = 0; i < boundary.size(); i++) { + auto c = boundary[i]; + if (!std::isalnum(c) && c != '-' && c != '_') { + valid = false; + break; + } + } + return valid; +} + +template +inline std::string +serialize_multipart_formdata_item_begin(const T &item, + const std::string &boundary) { + std::string body = "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + + return body; +} + +inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; } + +inline std::string +serialize_multipart_formdata_finish(const std::string &boundary) { + return "--" + boundary + "--\r\n"; +} + +inline std::string +serialize_multipart_formdata_get_content_type(const std::string &boundary) { + return "multipart/form-data; boundary=" + boundary; +} + +inline std::string +serialize_multipart_formdata(const MultipartFormDataItems &items, + const std::string &boundary, bool finish = true) { + std::string body; + + for (const auto &item : items) { + body += serialize_multipart_formdata_item_begin(item, boundary); + body += item.content + serialize_multipart_formdata_item_end(); + } + + if (finish) { body += serialize_multipart_formdata_finish(boundary); } + + return body; +} + +inline bool range_error(Request &req, Response &res) { + if (!req.ranges.empty() && 200 <= res.status && res.status < 300) { + ssize_t contant_len = static_cast( + res.content_length_ ? res.content_length_ : res.body.size()); + + ssize_t prev_first_pos = -1; + ssize_t prev_last_pos = -1; + size_t overwrapping_count = 0; + + // NOTE: The following Range check is based on '14.2. Range' in RFC 9110 + // 'HTTP Semantics' to avoid potential denial-of-service attacks. + // https://www.rfc-editor.org/rfc/rfc9110#section-14.2 + + // Too many ranges + if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; } + + for (auto &r : req.ranges) { + auto &first_pos = r.first; + auto &last_pos = r.second; + + if (first_pos == -1 && last_pos == -1) { + first_pos = 0; + last_pos = contant_len; + } + + if (first_pos == -1) { + first_pos = contant_len - last_pos; + last_pos = contant_len - 1; + } + + if (last_pos == -1) { last_pos = contant_len - 1; } + + // Range must be within content length + if (!(0 <= first_pos && first_pos <= last_pos && + last_pos <= contant_len - 1)) { + return true; + } + + // Ranges must be in ascending order + if (first_pos <= prev_first_pos) { return true; } + + // Request must not have more than two overlapping ranges + if (first_pos <= prev_last_pos) { + overwrapping_count++; + if (overwrapping_count > 2) { return true; } + } + + prev_first_pos = (std::max)(prev_first_pos, first_pos); + prev_last_pos = (std::max)(prev_last_pos, last_pos); + } + } + + return false; +} + +inline std::pair +get_range_offset_and_length(Range r, size_t content_length) { + assert(r.first != -1 && r.second != -1); + assert(0 <= r.first && r.first < static_cast(content_length)); + assert(r.first <= r.second && + r.second < static_cast(content_length)); + (void)(content_length); + return std::make_pair(r.first, static_cast(r.second - r.first) + 1); +} + +inline std::string make_content_range_header_field( + const std::pair &offset_and_length, size_t content_length) { + auto st = offset_and_length.first; + auto ed = st + offset_and_length.second - 1; + + std::string field = "bytes "; + field += std::to_string(st); + field += "-"; + field += std::to_string(ed); + field += "/"; + field += std::to_string(content_length); + return field; +} + +template +bool process_multipart_ranges_data(const Request &req, + const std::string &boundary, + const std::string &content_type, + size_t content_length, SToken stoken, + CToken ctoken, Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offset_and_length = + get_range_offset_and_length(req.ranges[i], content_length); + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset_and_length, content_length)); + ctoken("\r\n"); + ctoken("\r\n"); + + if (!content(offset_and_length.first, offset_and_length.second)) { + return false; + } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--"); + + return true; +} + +inline void make_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + size_t content_length, + std::string &data) { + process_multipart_ranges_data( + req, boundary, content_type, content_length, + [&](const std::string &token) { data += token; }, + [&](const std::string &token) { data += token; }, + [&](size_t offset, size_t length) { + assert(offset + length <= content_length); + data += res.body.substr(offset, length); + return true; + }); +} + +inline size_t get_multipart_ranges_data_length(const Request &req, + const std::string &boundary, + const std::string &content_type, + size_t content_length) { + size_t data_length = 0; + + process_multipart_ranges_data( + req, boundary, content_type, content_length, + [&](const std::string &token) { data_length += token.size(); }, + [&](const std::string &token) { data_length += token.size(); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; +} + +template +inline bool +write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + size_t content_length, const T &is_shutting_down) { + return process_multipart_ranges_data( + req, boundary, content_type, content_length, + [&](const std::string &token) { strm.write(token); }, + [&](const std::string &token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, + is_shutting_down); + }); +} + +inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || + req.method == "PRI" || req.method == "DELETE") { + return true; + } + // TODO: check if Content-Length is set + return false; +} + +inline bool has_crlf(const std::string &s) { + auto p = s.c_str(); + while (*p) { + if (*p == '\r' || *p == '\n') { return true; } + p++; + } + return false; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::string message_digest(const std::string &s, const EVP_MD *algo) { + auto context = std::unique_ptr( + EVP_MD_CTX_new(), EVP_MD_CTX_free); + + unsigned int hash_length = 0; + unsigned char hash[EVP_MAX_MD_SIZE]; + + EVP_DigestInit_ex(context.get(), algo, nullptr); + EVP_DigestUpdate(context.get(), s.c_str(), s.size()); + EVP_DigestFinal_ex(context.get(), hash, &hash_length); + + std::stringstream ss; + for (auto i = 0u; i < hash_length; ++i) { + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(hash[i]); + } + + return ss.str(); +} + +inline std::string MD5(const std::string &s) { + return message_digest(s, EVP_md5()); +} + +inline std::string SHA_256(const std::string &s) { + return message_digest(s, EVP_sha256()); +} + +inline std::string SHA_512(const std::string &s) { + return message_digest(s, EVP_sha512()); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#ifdef _WIN32 +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store +inline bool load_system_certs_on_windows(X509_STORE *store) { + auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); + if (!hStore) { return false; } + + auto result = false; + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != + nullptr) { + auto encoded_cert = + static_cast(pContext->pbCertEncoded); + + auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + result = true; + } + } + + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + return result; +} +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#if TARGET_OS_OSX +template +using CFObjectPtr = + std::unique_ptr::type, void (*)(CFTypeRef)>; + +inline void cf_object_ptr_deleter(CFTypeRef obj) { + if (obj) { CFRelease(obj); } +} + +inline bool retrieve_certs_from_keychain(CFObjectPtr &certs) { + CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef}; + CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, + kCFBooleanTrue}; + + CFObjectPtr query( + CFDictionaryCreate(nullptr, reinterpret_cast(keys), values, + sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks), + cf_object_ptr_deleter); + + if (!query) { return false; } + + CFTypeRef security_items = nullptr; + if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess || + CFArrayGetTypeID() != CFGetTypeID(security_items)) { + return false; + } + + certs.reset(reinterpret_cast(security_items)); + return true; +} + +inline bool retrieve_root_certs_from_keychain(CFObjectPtr &certs) { + CFArrayRef root_security_items = nullptr; + if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) { + return false; + } + + certs.reset(root_security_items); + return true; +} + +inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) { + auto result = false; + for (auto i = 0; i < CFArrayGetCount(certs); ++i) { + const auto cert = reinterpret_cast( + CFArrayGetValueAtIndex(certs, i)); + + if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; } + + CFDataRef cert_data = nullptr; + if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) != + errSecSuccess) { + continue; + } + + CFObjectPtr cert_data_ptr(cert_data, cf_object_ptr_deleter); + + auto encoded_cert = static_cast( + CFDataGetBytePtr(cert_data_ptr.get())); + + auto x509 = + d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get())); + + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + result = true; + } + } + + return result; +} + +inline bool load_system_certs_on_macos(X509_STORE *store) { + auto result = false; + CFObjectPtr certs(nullptr, cf_object_ptr_deleter); + if (retrieve_certs_from_keychain(certs) && certs) { + result = add_certs_to_x509_store(certs.get(), store); + } + + if (retrieve_root_certs_from_keychain(certs) && certs) { + result = add_certs_to_x509_store(certs.get(), store) || result; + } + + return result; +} +#endif // TARGET_OS_OSX +#endif // _WIN32 +#endif // CPPHTTPLIB_OPENSSL_SUPPORT + +#ifdef _WIN32 +class WSInit { +public: + WSInit() { + WSADATA wsaData; + if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true; + } + + ~WSInit() { + if (is_valid_) WSACleanup(); + } + + bool is_valid_ = false; +}; + +static WSInit wsinit_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::pair make_digest_authentication_header( + const Request &req, const std::map &auth, + size_t cnonce_count, const std::string &cnonce, const std::string &username, + const std::string &password, bool is_proxy = false) { + std::string nc; + { + std::stringstream ss; + ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count; + nc = ss.str(); + } + + std::string qop; + if (auth.find("qop") != auth.end()) { + qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else if (qop.find("auth") != std::string::npos) { + qop = "auth"; + } else { + qop.clear(); + } + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } + + std::string response; + { + auto H = algo == "SHA-256" ? detail::SHA_256 + : algo == "SHA-512" ? detail::SHA_512 + : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { A2 += ":" + H(req.body); } + + if (qop.empty()) { + response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2)); + } else { + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + + ":" + qop + ":" + H(A2)); + } + } + + auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : ""; + + auto field = "Digest username=\"" + username + "\", realm=\"" + + auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + + "\", uri=\"" + req.path + "\", algorithm=" + algo + + (qop.empty() ? ", response=\"" + : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + + cnonce + "\", response=\"") + + response + "\"" + + (opaque.empty() ? "" : ", opaque=\"" + opaque + "\""); + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + +inline bool parse_www_authenticate(const Response &res, + std::map &auth, + bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + const auto &m = *i; + auto key = s.substr(static_cast(m.position(1)), + static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), + static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), + static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; +} + +class ContentProviderAdapter { +public: + explicit ContentProviderAdapter( + ContentProviderWithoutLength &&content_provider) + : content_provider_(content_provider) {} + + bool operator()(size_t offset, size_t, DataSink &sink) { + return content_provider_(offset, sink); + } + +private: + ContentProviderWithoutLength content_provider_; +}; + +} // namespace detail + +inline std::string hosted_at(const std::string &hostname) { + std::vector addrs; + hosted_at(hostname, addrs); + if (addrs.empty()) { return std::string(); } + return addrs[0]; +} + +inline void hosted_at(const std::string &hostname, + std::vector &addrs) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) { +#if defined __linux__ && !defined __ANDROID__ + res_init(); +#endif + return; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &addr = + *reinterpret_cast(rp->ai_addr); + std::string ip; + auto dummy = -1; + if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip, + dummy)) { + addrs.push_back(ip); + } + } + + freeaddrinfo(result); +} + +inline std::string append_query_params(const std::string &path, + const Params ¶ms) { + std::string path_with_query = path; + const static std::regex re("[^?]+\\?.*"); + auto delm = std::regex_match(path, re) ? '&' : '?'; + path_with_query += delm + detail::params_to_query_str(params); + return path_with_query; +} + +// Header utilities +inline std::pair +make_range_header(const Ranges &ranges) { + std::string field = "bytes="; + auto i = 0; + for (const auto &r : ranges) { + if (i != 0) { field += ", "; } + if (r.first != -1) { field += std::to_string(r.first); } + field += '-'; + if (r.second != -1) { field += std::to_string(r.second); } + i++; + } + return std::make_pair("Range", std::move(field)); +} + +inline std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, bool is_proxy) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); +} + +inline std::pair +make_bearer_token_authentication_header(const std::string &token, + bool is_proxy = false) { + auto field = "Bearer " + token; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); +} + +// Request implementation +inline bool Request::has_header(const std::string &key) const { + return detail::has_header(headers, key); +} + +inline std::string Request::get_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Request::get_header_value_count(const std::string &key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Request::set_header(const std::string &key, + const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline bool Request::has_param(const std::string &key) const { + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const std::string &key, + size_t id) const { + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second; } + return std::string(); +} + +inline size_t Request::get_param_value_count(const std::string &key) const { + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.rfind("multipart/form-data", 0); +} + +inline bool Request::has_file(const std::string &key) const { + return files.find(key) != files.end(); +} + +inline MultipartFormData Request::get_file_value(const std::string &key) const { + auto it = files.find(key); + if (it != files.end()) { return it->second; } + return MultipartFormData(); +} + +inline std::vector +Request::get_file_values(const std::string &key) const { + std::vector values; + auto rng = files.equal_range(key); + for (auto it = rng.first; it != rng.second; it++) { + values.push_back(it->second); + } + return values; +} + +// Response implementation +inline bool Response::has_header(const std::string &key) const { + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Response::get_header_value_count(const std::string &key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Response::set_header(const std::string &key, + const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Response::set_redirect(const std::string &url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = StatusCode::Found_302; + } + } +} + +inline void Response::set_content(const char *s, size_t n, + const std::string &content_type) { + body.assign(s, n); + + auto rng = headers.equal_range("Content-Type"); + headers.erase(rng.first, rng.second); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(const std::string &s, + const std::string &content_type) { + set_content(s.data(), s.size(), content_type); +} + +inline void Response::set_content(std::string &&s, + const std::string &content_type) { + body = std::move(s); + + auto rng = headers.equal_range("Content-Type"); + headers.erase(rng.first, rng.second); + set_header("Content-Type", content_type); +} + +inline void Response::set_content_provider( + size_t in_length, const std::string &content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = in_length; + if (in_length > 0) { content_provider_ = std::move(provider); } + content_provider_resource_releaser_ = std::move(resource_releaser); + is_chunked_content_provider_ = false; +} + +inline void Response::set_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = std::move(resource_releaser); + is_chunked_content_provider_ = false; +} + +inline void Response::set_chunked_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = std::move(resource_releaser); + is_chunked_content_provider_ = true; +} + +// Result implementation +inline bool Result::has_request_header(const std::string &key) const { + return request_headers_.find(key) != request_headers_.end(); +} + +inline std::string Result::get_request_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(request_headers_, key, id, ""); +} + +inline size_t +Result::get_request_header_value_count(const std::string &key) const { + auto r = request_headers_.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +// Stream implementation +inline ssize_t Stream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); +} + +namespace detail { + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {} + +inline SocketStream::~SocketStream() = default; + +inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && + is_socket_alive(sock_); +} + +inline ssize_t SocketStream::read(char *ptr, size_t size) { +#ifdef _WIN32 + size = + (std::min)(size, static_cast((std::numeric_limits::max)())); +#else + size = (std::min)(size, + static_cast((std::numeric_limits::max)())); +#endif + + if (read_buff_off_ < read_buff_content_size_) { + auto remaining_size = read_buff_content_size_ - read_buff_off_; + if (size <= remaining_size) { + memcpy(ptr, read_buff_.data() + read_buff_off_, size); + read_buff_off_ += size; + return static_cast(size); + } else { + memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size); + read_buff_off_ += remaining_size; + return static_cast(remaining_size); + } + } + + if (!is_readable()) { return -1; } + + read_buff_off_ = 0; + read_buff_content_size_ = 0; + + if (size < read_buff_size_) { + auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, + CPPHTTPLIB_RECV_FLAGS); + if (n <= 0) { + return n; + } else if (n <= static_cast(size)) { + memcpy(ptr, read_buff_.data(), static_cast(n)); + return n; + } else { + memcpy(ptr, read_buff_.data(), size); + read_buff_off_ = size; + read_buff_content_size_ = static_cast(n); + return static_cast(size); + } + } else { + return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); + } +} + +inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { return -1; } + +#if defined(_WIN32) && !defined(_WIN64) + size = + (std::min)(size, static_cast((std::numeric_limits::max)())); +#endif + + return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); +} + +inline void SocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline void SocketStream::get_local_ip_and_port(std::string &ip, + int &port) const { + return detail::get_local_ip_and_port(sock_, ip, port); +} + +inline socket_t SocketStream::socket() const { return sock_; } + +// Buffer stream implementation +inline bool BufferStream::is_readable() const { return true; } + +inline bool BufferStream::is_writable() const { return true; } + +inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1910 + auto len_read = buffer._Copy_s(ptr, size, size, position); +#else + auto len_read = buffer.copy(ptr, size, position); +#endif + position += static_cast(len_read); + return static_cast(len_read); +} + +inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); +} + +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + +inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + +inline socket_t BufferStream::socket() const { return 0; } + +inline const std::string &BufferStream::get_buffer() const { return buffer; } + +inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) { + // One past the last ending position of a path param substring + std::size_t last_param_end = 0; + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS + // Needed to ensure that parameter names are unique during matcher + // construction + // If exceptions are disabled, only last duplicate path + // parameter will be set + std::unordered_set param_name_set; +#endif + + while (true) { + const auto marker_pos = pattern.find(marker, last_param_end); + if (marker_pos == std::string::npos) { break; } + + static_fragments_.push_back( + pattern.substr(last_param_end, marker_pos - last_param_end)); + + const auto param_name_start = marker_pos + 1; + + auto sep_pos = pattern.find(separator, param_name_start); + if (sep_pos == std::string::npos) { sep_pos = pattern.length(); } + + auto param_name = + pattern.substr(param_name_start, sep_pos - param_name_start); + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS + if (param_name_set.find(param_name) != param_name_set.cend()) { + std::string msg = "Encountered path parameter '" + param_name + + "' multiple times in route pattern '" + pattern + "'."; + throw std::invalid_argument(msg); + } +#endif + + param_names_.push_back(std::move(param_name)); + + last_param_end = sep_pos + 1; + } + + if (last_param_end < pattern.length()) { + static_fragments_.push_back(pattern.substr(last_param_end)); + } +} + +inline bool PathParamsMatcher::match(Request &request) const { + request.matches = std::smatch(); + request.path_params.clear(); + request.path_params.reserve(param_names_.size()); + + // One past the position at which the path matched the pattern last time + std::size_t starting_pos = 0; + for (size_t i = 0; i < static_fragments_.size(); ++i) { + const auto &fragment = static_fragments_[i]; + + if (starting_pos + fragment.length() > request.path.length()) { + return false; + } + + // Avoid unnecessary allocation by using strncmp instead of substr + + // comparison + if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(), + fragment.length()) != 0) { + return false; + } + + starting_pos += fragment.length(); + + // Should only happen when we have a static fragment after a param + // Example: '/users/:id/subscriptions' + // The 'subscriptions' fragment here does not have a corresponding param + if (i >= param_names_.size()) { continue; } + + auto sep_pos = request.path.find(separator, starting_pos); + if (sep_pos == std::string::npos) { sep_pos = request.path.length(); } + + const auto ¶m_name = param_names_[i]; + + request.path_params.emplace( + param_name, request.path.substr(starting_pos, sep_pos - starting_pos)); + + // Mark everythin up to '/' as matched + starting_pos = sep_pos + 1; + } + // Returns false if the path is longer than the pattern + return starting_pos >= request.path.length(); +} + +inline bool RegexMatcher::match(Request &request) const { + request.path_params.clear(); + return std::regex_match(request.path, request.matches, regex_); +} + +} // namespace detail + +// HTTP server implementation +inline Server::Server() + : new_task_queue( + [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() = default; + +inline std::unique_ptr +Server::make_matcher(const std::string &pattern) { + if (pattern.find("/:") != std::string::npos) { + return detail::make_unique(pattern); + } else { + return detail::make_unique(pattern); + } +} + +inline Server &Server::Get(const std::string &pattern, Handler handler) { + get_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Post(const std::string &pattern, Handler handler) { + post_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Post(const std::string &pattern, + HandlerWithContentReader handler) { + post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Put(const std::string &pattern, Handler handler) { + put_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Put(const std::string &pattern, + HandlerWithContentReader handler) { + put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Patch(const std::string &pattern, Handler handler) { + patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Patch(const std::string &pattern, + HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Delete(const std::string &pattern, Handler handler) { + delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Delete(const std::string &pattern, + HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Options(const std::string &pattern, Handler handler) { + options_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline bool Server::set_base_dir(const std::string &dir, + const std::string &mount_point) { + return set_mount_point(mount_point, dir); +} + +inline bool Server::set_mount_point(const std::string &mount_point, + const std::string &dir, Headers headers) { + if (detail::is_dir(dir)) { + std::string mnt = !mount_point.empty() ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.push_back({mnt, dir, std::move(headers)}); + return true; + } + } + return false; +} + +inline bool Server::remove_mount_point(const std::string &mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->mount_point == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; +} + +inline Server & +Server::set_file_extension_and_mimetype_mapping(const std::string &ext, + const std::string &mime) { + file_extension_and_mimetype_map_[ext] = mime; + return *this; +} + +inline Server &Server::set_default_file_mimetype(const std::string &mime) { + default_file_mimetype_ = mime; + return *this; +} + +inline Server &Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_error_handler_core(HandlerWithResponse handler, + std::true_type) { + error_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_error_handler_core(Handler handler, + std::false_type) { + error_handler_ = [handler](const Request &req, Response &res) { + handler(req, res); + return HandlerResponse::Handled; + }; + return *this; +} + +inline Server &Server::set_exception_handler(ExceptionHandler handler) { + exception_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { + pre_routing_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_post_routing_handler(Handler handler) { + post_routing_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_logger(Logger logger) { + logger_ = std::move(logger); + return *this; +} + +inline Server & +Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_address_family(int family) { + address_family_ = family; + return *this; +} + +inline Server &Server::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; + return *this; +} + +inline Server &Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); + return *this; +} + +inline Server &Server::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); + return *this; +} + +inline Server &Server::set_header_writer( + std::function const &writer) { + header_writer_ = writer; + return *this; +} + +inline Server &Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; + return *this; +} + +inline Server &Server::set_keep_alive_timeout(time_t sec) { + keep_alive_timeout_sec_ = sec; + return *this; +} + +inline Server &Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; + return *this; +} + +inline Server &Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; + return *this; +} + +inline Server &Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; + return *this; +} + +inline Server &Server::set_payload_max_length(size_t length) { + payload_max_length_ = length; + return *this; +} + +inline bool Server::bind_to_port(const std::string &host, int port, + int socket_flags) { + return bind_internal(host, port, socket_flags) >= 0; +} +inline int Server::bind_to_any_port(const std::string &host, int socket_flags) { + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { + auto se = detail::scope_exit([&]() { done_ = true; }); + return listen_internal(); +} + +inline bool Server::listen(const std::string &host, int port, + int socket_flags) { + auto se = detail::scope_exit([&]() { done_ = true; }); + return bind_to_port(host, port, socket_flags) && listen_internal(); +} + +inline bool Server::is_running() const { return is_running_; } + +inline void Server::wait_until_ready() const { + while (!is_running() && !done_) { + std::this_thread::sleep_for(std::chrono::milliseconds{1}); + } +} + +inline void Server::stop() { + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + } +} + +inline bool Server::parse_request_line(const char *s, Request &req) const { + auto len = strlen(s); + if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; } + len -= 2; + + { + size_t count = 0; + + detail::split(s, s + len, ' ', [&](const char *b, const char *e) { + switch (count) { + case 0: req.method = std::string(b, e); break; + case 1: req.target = std::string(b, e); break; + case 2: req.version = std::string(b, e); break; + default: break; + } + count++; + }); + + if (count != 3) { return false; } + } + + static const std::set methods{ + "GET", "HEAD", "POST", "PUT", "DELETE", + "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"}; + + if (methods.find(req.method) == methods.end()) { return false; } + + if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; } + + { + // Skip URL fragment + for (size_t i = 0; i < req.target.size(); i++) { + if (req.target[i] == '#') { + req.target.erase(i); + break; + } + } + + detail::divide(req.target, '?', + [&](const char *lhs_data, std::size_t lhs_size, + const char *rhs_data, std::size_t rhs_size) { + req.path = detail::decode_url( + std::string(lhs_data, lhs_size), false); + detail::parse_query_text(rhs_data, rhs_size, req.params); + }); + } + + return true; +} + +inline bool Server::write_response(Stream &strm, bool close_connection, + Request &req, Response &res) { + // NOTE: `req.ranges` should be empty, otherwise it will be applied + // incorrectly to the error content. + req.ranges.clear(); + return write_response_core(strm, close_connection, req, res, false); +} + +inline bool Server::write_response_with_content(Stream &strm, + bool close_connection, + const Request &req, + Response &res) { + return write_response_core(strm, close_connection, req, res, true); +} + +inline bool Server::write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_ && + error_handler_(req, res) == HandlerResponse::Handled) { + need_apply_ranges = true; + } + + std::string content_type; + std::string boundary; + if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); } + + // Prepare additional headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } else { + std::stringstream ss; + ss << "timeout=" << keep_alive_timeout_sec_ + << ", max=" << keep_alive_max_count_; + res.set_header("Keep-Alive", ss.str()); + } + + if (!res.has_header("Content-Type") && + (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Content-Length") && res.body.empty() && + !res.content_length_ && !res.content_provider_) { + res.set_header("Content-Length", "0"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + if (post_routing_handler_) { post_routing_handler_(req, res); } + + // Response line and headers + { + detail::BufferStream bstrm; + + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, + status_message(res.status))) { + return false; + } + + if (!header_writer_(bstrm, res.headers)) { return false; } + + // Flush buffer + auto &data = bstrm.get_buffer(); + detail::write_data(strm, data.data(), data.size()); + } + + // Body + auto ret = true; + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!detail::write_data(strm, res.body.data(), res.body.size())) { + ret = false; + } + } else if (res.content_provider_) { + if (write_content_with_provider(strm, req, res, boundary, content_type)) { + res.content_provider_success_ = true; + } else { + ret = false; + } + } + } + + // Log + if (logger_) { logger_(req, res); } + + return ret; +} + +inline bool +Server::write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type) { + auto is_shutting_down = [this]() { + return this->svr_sock_ == INVALID_SOCKET; + }; + + if (res.content_length_ > 0) { + if (req.ranges.empty()) { + return detail::write_content(strm, res.content_provider_, 0, + res.content_length_, is_shutting_down); + } else if (req.ranges.size() == 1) { + auto offset_and_length = detail::get_range_offset_and_length( + req.ranges[0], res.content_length_); + + return detail::write_content(strm, res.content_provider_, + offset_and_length.first, + offset_and_length.second, is_shutting_down); + } else { + return detail::write_multipart_ranges_data( + strm, req, res, boundary, content_type, res.content_length_, + is_shutting_down); + } + } else { + if (res.is_chunked_content_provider_) { + auto type = detail::encoding_type(req, res); + + std::unique_ptr compressor; + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); +#endif + } else { + compressor = detail::make_unique(); + } + assert(compressor != nullptr); + + return detail::write_content_chunked(strm, res.content_provider_, + is_shutting_down, *compressor); + } else { + return detail::write_content_without_length(strm, res.content_provider_, + is_shutting_down); + } + } +} + +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + auto file_count = 0; + if (read_content_core( + strm, req, res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { return false; } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) { + return false; + } + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { return false; } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) { + res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414? + return false; + } + detail::parse_query_text(req.body, req.params); + } + return true; + } + return false; +} + +inline bool Server::read_content_with_content_receiver( + Stream &strm, Request &req, Response &res, ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, std::move(receiver), + std::move(multipart_header), + std::move(multipart_receiver)); +} + +inline bool +Server::read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) const { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiverWithProgress out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = StatusCode::BadRequest_400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = (std::min)(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, multipart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, + multipart_header); + }; + } else { + out = [receiver](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { return receiver(buf, n); }; + } + + if (req.method == "DELETE" && !req.has_header("Content-Length")) { + return true; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, + out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = StatusCode::BadRequest_400; + return false; + } + } + + return true; +} + +inline bool Server::handle_file_request(const Request &req, Response &res, + bool head) { + for (const auto &entry : base_dirs_) { + // Prefix match + if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) { + std::string sub_path = "/" + req.path.substr(entry.mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = entry.base_dir + sub_path; + if (path.back() == '/') { path += "index.html"; } + + if (detail::is_file(path)) { + for (const auto &kv : entry.headers) { + res.set_header(kv.first, kv.second); + } + + auto mm = std::make_shared(path.c_str()); + if (!mm->is_open()) { return false; } + + res.set_content_provider( + mm->size(), + detail::find_content_type(path, file_extension_and_mimetype_map_, + default_file_mimetype_), + [mm](size_t offset, size_t length, DataSink &sink) -> bool { + sink.write(mm->data() + offset, length); + return true; + }); + + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + + return true; + } + } + } + } + return false; +} + +inline socket_t +Server::create_server_socket(const std::string &host, int port, + int socket_flags, + SocketOptions socket_options) const { + return detail::create_socket( + host, std::string(), port, address_family_, socket_flags, tcp_nodelay_, + std::move(socket_options), + [](socket_t sock, struct addrinfo &ai) -> bool { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; } + return true; + }); +} + +inline int Server::bind_internal(const std::string &host, int port, + int socket_flags) { + if (!is_valid()) { return -1; } + + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == INVALID_SOCKET) { return -1; } + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_, reinterpret_cast(&addr), + &addr_len) == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +inline bool Server::listen_internal() { + auto ret = true; + is_running_ = true; + auto se = detail::scope_exit([&]() { is_running_ = false; }); + + { + std::unique_ptr task_queue(new_task_queue()); + + while (svr_sock_ != INVALID_SOCKET) { +#ifndef _WIN32 + if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { +#endif + auto val = detail::select_read(svr_sock_, idle_interval_sec_, + idle_interval_usec_); + if (val == 0) { // Timeout + task_queue->on_idle(); + continue; + } +#ifndef _WIN32 + } +#endif + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (errno == EMFILE) { + // The per-process limit of open file descriptors has been reached. + // Try to accept new connections after a short sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } else if (errno == EINTR || errno == EAGAIN) { + continue; + } + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + { +#ifdef _WIN32 + auto timeout = static_cast(read_timeout_sec_ * 1000 + + read_timeout_usec_ / 1000); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec_); + tv.tv_usec = static_cast(read_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + { + +#ifdef _WIN32 + auto timeout = static_cast(write_timeout_sec_ * 1000 + + write_timeout_usec_ / 1000); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec_); + tv.tv_usec = static_cast(write_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + + if (!task_queue->enqueue( + [this, sock]() { process_and_close_socket(sock); })) { + detail::shutdown_socket(sock); + detail::close_socket(sock); + } + } + + task_queue->shutdown(); + } + + return ret; +} + +inline bool Server::routing(Request &req, Response &res, Stream &strm) { + if (pre_routing_handler_ && + pre_routing_handler_(req, res) == HandlerResponse::Handled) { + return true; + } + + // File handler + auto is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && + handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver( + strm, req, res, std::move(receiver), nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, + std::move(header), + std::move(receiver)); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { return false; } + } + + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } + + res.status = StatusCode::BadRequest_400; + return false; +} + +inline bool Server::dispatch_request(Request &req, Response &res, + const Handlers &handlers) const { + for (const auto &x : handlers) { + const auto &matcher = x.first; + const auto &handler = x.second; + + if (matcher->match(req)) { + handler(req, res); + return true; + } + } + return false; +} + +inline void Server::apply_ranges(const Request &req, Response &res, + std::string &content_type, + std::string &boundary) const { + if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) { + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + boundary = detail::make_multipart_data_boundary(); + + res.set_header("Content-Type", + "multipart/byteranges; boundary=" + boundary); + } + + auto type = detail::encoding_type(req, res); + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offset_and_length = detail::get_range_offset_and_length( + req.ranges[0], res.content_length_); + + length = offset_and_length.second; + + auto content_range = detail::make_content_range_header_field( + offset_and_length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length( + req, boundary, content_type, res.content_length_); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + if (res.is_chunked_content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + if (type == detail::EncodingType::Gzip) { + res.set_header("Content-Encoding", "gzip"); + } else if (type == detail::EncodingType::Brotli) { + res.set_header("Content-Encoding", "br"); + } + } + } + } + } else { + if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) { + ; + } else if (req.ranges.size() == 1) { + auto offset_and_length = + detail::get_range_offset_and_length(req.ranges[0], res.body.size()); + auto offset = offset_and_length.first; + auto length = offset_and_length.second; + + auto content_range = detail::make_content_range_header_field( + offset_and_length, res.body.size()); + res.set_header("Content-Range", content_range); + + assert(offset + length <= res.body.size()); + res.body = res.body.substr(offset, length); + } else { + std::string data; + detail::make_multipart_ranges_data(req, res, boundary, content_type, + res.body.size(), data); + res.body.swap(data); + } + + if (type != detail::EncodingType::None) { + std::unique_ptr compressor; + std::string content_encoding; + + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); + content_encoding = "gzip"; +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); + content_encoding = "br"; +#endif + } + + if (compressor) { + std::string compressed; + if (compressor->compress(res.body.data(), res.body.size(), true, + [&](const char *data, size_t data_len) { + compressed.append(data, data_len); + return true; + })) { + res.body.swap(compressed); + res.set_header("Content-Encoding", content_encoding); + } + } + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } +} + +inline bool Server::dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) const { + for (const auto &x : handlers) { + const auto &matcher = x.first; + const auto &handler = x.second; + + if (matcher->match(req)) { + handler(req, res, content_reader); + return true; + } + } + return false; +} + +inline bool +Server::process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + // Connection has been closed on client + if (!line_reader.getline()) { return false; } + + Request req; + + Response res; + res.version = "HTTP/1.1"; + res.headers = default_headers_; + +#ifdef _WIN32 + // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL). +#else +#ifndef CPPHTTPLIB_USE_POLL + // Socket file descriptor exceeded FD_SETSIZE... + if (strm.socket() >= FD_SETSIZE) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = StatusCode::InternalServerError_500; + return write_response(strm, close_connection, req, res); + } +#endif +#endif + + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = StatusCode::UriTooLong_414; + return write_response(strm, close_connection, req, res); + } + + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || + !detail::read_headers(strm, req.headers)) { + res.status = StatusCode::BadRequest_400; + return write_response(strm, close_connection, req, res); + } + + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } + + if (req.version == "HTTP/1.0" && + req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } + + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + + strm.get_local_ip_and_port(req.local_addr, req.local_port); + req.set_header("LOCAL_ADDR", req.local_addr); + req.set_header("LOCAL_PORT", std::to_string(req.local_port)); + + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + res.status = StatusCode::RangeNotSatisfiable_416; + return write_response(strm, close_connection, req, res); + } + } + + if (setup_request) { setup_request(req); } + + if (req.get_header_value("Expect") == "100-continue") { + int status = StatusCode::Continue_100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case StatusCode::Continue_100: + case StatusCode::ExpectationFailed_417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, + status_message(status)); + break; + default: return write_response(strm, close_connection, req, res); + } + } + + // Routing + auto routed = false; +#ifdef CPPHTTPLIB_NO_EXCEPTIONS + routed = routing(req, res, strm); +#else + try { + routed = routing(req, res, strm); + } catch (std::exception &e) { + if (exception_handler_) { + auto ep = std::current_exception(); + exception_handler_(req, res, ep); + routed = true; + } else { + res.status = StatusCode::InternalServerError_500; + std::string val; + auto s = e.what(); + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case '\r': val += "\\r"; break; + case '\n': val += "\\n"; break; + default: val += s[i]; break; + } + } + res.set_header("EXCEPTION_WHAT", val); + } + } catch (...) { + if (exception_handler_) { + auto ep = std::current_exception(); + exception_handler_(req, res, ep); + routed = true; + } else { + res.status = StatusCode::InternalServerError_500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + } +#endif + if (routed) { + if (res.status == -1) { + res.status = req.ranges.empty() ? StatusCode::OK_200 + : StatusCode::PartialContent_206; + } + + if (detail::range_error(req, res)) { + res.body.clear(); + res.content_length_ = 0; + res.content_provider_ = nullptr; + res.status = StatusCode::RangeNotSatisfiable_416; + return write_response(strm, close_connection, req, res); + } + + return write_response_with_content(strm, close_connection, req, res); + } else { + if (res.status == -1) { res.status = StatusCode::NotFound_404; } + + return write_response(strm, close_connection, req, res); + } +} + +inline bool Server::is_valid() const { return true; } + +inline bool Server::process_and_close_socket(socket_t sock) { + auto ret = detail::process_server_socket( + svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this](Stream &strm, bool close_connection, bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + nullptr); + }); + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// HTTP client implementation +inline ClientImpl::ClientImpl(const std::string &host) + : ClientImpl(host, 80, std::string(), std::string()) {} + +inline ClientImpl::ClientImpl(const std::string &host, int port) + : ClientImpl(host, port, std::string(), std::string()) {} + +inline ClientImpl::ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : host_(host), port_(port), + host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)), + client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} + +inline ClientImpl::~ClientImpl() { + std::lock_guard guard(socket_mutex_); + shutdown_socket(socket_); + close_socket(socket_); +} + +inline bool ClientImpl::is_valid() const { return true; } + +inline void ClientImpl::copy_settings(const ClientImpl &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; + bearer_token_auth_token_ = rhs.bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + url_encode_ = rhs.url_encode_; + address_family_ = rhs.address_family_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; + proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + ca_cert_file_path_ = rhs.ca_cert_file_path_; + ca_cert_dir_path_ = rhs.ca_cert_dir_path_; + ca_cert_store_ = rhs.ca_cert_store_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + server_certificate_verification_ = rhs.server_certificate_verification_; +#endif + logger_ = rhs.logger_; +} + +inline socket_t ClientImpl::create_client_socket(Error &error) const { + if (!proxy_host_.empty() && proxy_port_ != -1) { + return detail::create_client_socket( + proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_, + socket_options_, connection_timeout_sec_, connection_timeout_usec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, interface_, error); + } + + // Check is custom IP specified for host_ + std::string ip; + auto it = addr_map_.find(host_); + if (it != addr_map_.end()) { ip = it->second; } + + return detail::create_client_socket( + host_, ip, port_, address_family_, tcp_nodelay_, socket_options_, + connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, + error); +} + +inline bool ClientImpl::create_and_connect_socket(Socket &socket, + Error &error) { + auto sock = create_client_socket(error); + if (sock == INVALID_SOCKET) { return false; } + socket.sock = sock; + return true; +} + +inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, + bool /*shutdown_gracefully*/) { + // If there are any requests in flight from threads other than us, then it's + // a thread-unsafe race because individual ssl* objects are not thread-safe. + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); +} + +inline void ClientImpl::shutdown_socket(Socket &socket) const { + if (socket.sock == INVALID_SOCKET) { return; } + detail::shutdown_socket(socket.sock); +} + +inline void ClientImpl::close_socket(Socket &socket) { + // If there are requests in flight in another thread, usually closing + // the socket will be fine and they will simply receive an error when + // using the closed socket, but it is still a bug since rarely the OS + // may reassign the socket id to be used for a new socket, and then + // suddenly they will be operating on a live socket that is different + // than the one they intended! + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); + + // It is also a bug if this happens while SSL is still active +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + assert(socket.ssl == nullptr); +#endif + if (socket.sock == INVALID_SOCKET) { return; } + detail::close_socket(socket.sock); + socket.sock = INVALID_SOCKET; +} + +inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, + Response &res) const { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { return false; } + +#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n"); +#else + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); +#endif + + std::cmatch m; + if (!std::regex_match(line_reader.ptr(), m, re)) { + return req.method == "CONNECT"; + } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + + // Ignore '100 Continue' + while (res.status == StatusCode::Continue_100) { + if (!line_reader.getline()) { return false; } // CRLF + if (!line_reader.getline()) { return false; } // next response line + + if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + } + + return true; +} + +inline bool ClientImpl::send(Request &req, Response &res, Error &error) { + std::lock_guard request_mutex_guard(request_mutex_); + auto ret = send_(req, res, error); + if (error == Error::SSLPeerCouldBeClosed_) { + assert(!ret); + ret = send_(req, res, error); + } + return ret; +} + +inline bool ClientImpl::send_(Request &req, Response &res, Error &error) { + { + std::lock_guard guard(socket_mutex_); + + // Set this to false immediately - if it ever gets set to true by the end of + // the request, we know another thread instructed us to close the socket. + socket_should_be_closed_when_request_is_done_ = false; + + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::is_socket_alive(socket_.sock); + if (!is_alive) { + // Attempt to avoid sigpipe by shutting down nongracefully if it seems + // like the other side has already closed the connection Also, there + // cannot be any requests in flight from other threads since we locked + // request_mutex_, so safe to close everything immediately + const bool shutdown_gracefully = false; + shutdown_ssl(socket_, shutdown_gracefully); + shutdown_socket(socket_); + close_socket(socket_); + } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_, error)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty() && proxy_port_ != -1) { + auto success = false; + if (!scli.connect_with_proxy(socket_, res, success, error)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_, error)) { return false; } + } +#endif + } + + // Mark the current socket as being in use so that it cannot be closed by + // anyone else while this request is ongoing, even though we will be + // releasing the mutex. + if (socket_requests_in_flight_ > 1) { + assert(socket_requests_are_from_thread_ == std::this_thread::get_id()); + } + socket_requests_in_flight_ += 1; + socket_requests_are_from_thread_ = std::this_thread::get_id(); + } + + for (const auto &header : default_headers_) { + if (req.headers.find(header.first) == req.headers.end()) { + req.headers.insert(header); + } + } + + auto ret = false; + auto close_connection = !keep_alive_; + + auto se = detail::scope_exit([&]() { + // Briefly lock mutex in order to mark that a request is no longer ongoing + std::lock_guard guard(socket_mutex_); + socket_requests_in_flight_ -= 1; + if (socket_requests_in_flight_ <= 0) { + assert(socket_requests_in_flight_ == 0); + socket_requests_are_from_thread_ = std::thread::id(); + } + + if (socket_should_be_closed_when_request_is_done_ || close_connection || + !ret) { + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + }); + + ret = process_socket(socket_, [&](Stream &strm) { + return handle_request(strm, req, res, close_connection, error); + }); + + if (!ret) { + if (error == Error::Success) { error = Error::Unknown; } + } + + return ret; +} + +inline Result ClientImpl::send(const Request &req) { + auto req2 = req; + return send_(std::move(req2)); +} + +inline Result ClientImpl::send_(Request &&req) { + auto res = detail::make_unique(); + auto error = Error::Success; + auto ret = send(req, *res, error); + return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)}; +} + +inline bool ClientImpl::handle_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + if (req.path.empty()) { + error = Error::Connection; + return false; + } + + auto req_save = req; + + bool ret; + + if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { + auto req2 = req; + req2.path = "http://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection, error); + req = req2; + req.path = req_save.path; + } else { + ret = process_request(strm, req, res, close_connection, error); + } + + if (!ret) { return false; } + + if (res.get_header_value("Connection") == "close" || + (res.version == "HTTP/1.0" && res.reason != "Connection established")) { + // TODO this requires a not-entirely-obvious chain of calls to be correct + // for this to be safe. + + // This is safe to call because handle_request is only called by send_ + // which locks the request mutex during the process. It would be a bug + // to call it from a different thread since it's a thread-safety issue + // to do these things to the socket if another thread is using the socket. + std::lock_guard guard(socket_mutex_); + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + + if (300 < res.status && res.status < 400 && follow_location_) { + req = req_save; + ret = redirect(req, res, error); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == StatusCode::Unauthorized_401 || + res.status == StatusCode::ProxyAuthenticationRequired_407) && + req.authorization_count_ < 5) { + auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407; + const auto &username = + is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = + is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + new_req.headers.erase(is_proxy ? "Proxy-Authorization" + : "Authorization"); + new_req.headers.insert(detail::make_digest_authentication_header( + req, auth, new_req.authorization_count_, detail::random_string(10), + username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res, error); + if (ret) { res = new_res; } + } + } + } +#endif + + return ret; +} + +inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { + if (req.redirect_count_ == 0) { + error = Error::ExceedRedirectCount; + return false; + } + + auto location = res.get_header_value("location"); + if (location.empty()) { return false; } + + const static std::regex re( + R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { return false; } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + if (next_host.empty()) { next_host = m[3].str(); } + auto port_str = m[4].str(); + auto next_path = m[5].str(); + auto next_query = m[6].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { next_scheme = scheme; } + if (next_host.empty()) { next_host = host_; } + if (next_path.empty()) { next_path = "/"; } + + auto path = detail::decode_url(next_path, true) + next_query; + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, path, location, error); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host, next_port); + cli.copy_settings(*this); + if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); } + return detail::redirect(cli, req, res, path, location, error); +#else + return false; +#endif + } else { + ClientImpl cli(next_host, next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, path, location, error); + } + } +} + +inline bool ClientImpl::write_content_with_provider(Stream &strm, + const Request &req, + Error &error) const { + auto is_shutting_down = []() { return false; }; + + if (req.is_chunked_content_provider_) { + // TODO: Brotli support + std::unique_ptr compressor; +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + compressor = detail::make_unique(); + } else +#endif + { + compressor = detail::make_unique(); + } + + return detail::write_content_chunked(strm, req.content_provider_, + is_shutting_down, *compressor, error); + } else { + return detail::write_content(strm, req.content_provider_, 0, + req.content_length_, is_shutting_down, error); + } +} + +inline bool ClientImpl::write_request(Stream &strm, Request &req, + bool close_connection, Error &error) { + // Prepare additional headers + if (close_connection) { + if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + } + } + + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + req.set_header("Host", host_); + } else { + req.set_header("Host", host_and_port_); + } + } else { + if (port_ == 80) { + req.set_header("Host", host_); + } else { + req.set_header("Host", host_and_port_); + } + } + } + + if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); } + +#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT + if (!req.has_header("User-Agent")) { + auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION; + req.set_header("User-Agent", agent); + } +#endif + + if (req.body.empty()) { + if (req.content_provider_) { + if (!req.is_chunked_content_provider_) { + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.content_length_); + req.set_header("Content-Length", length); + } + } + } else { + if (req.method == "POST" || req.method == "PUT" || + req.method == "PATCH") { + req.set_header("Content-Length", "0"); + } + } + } else { + if (!req.has_header("Content-Type")) { + req.set_header("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length); + } + } + + if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) { + if (!req.has_header("Authorization")) { + req.headers.insert(make_basic_authentication_header( + basic_auth_username_, basic_auth_password_, false)); + } + } + + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + if (!req.has_header("Proxy-Authorization")) { + req.headers.insert(make_basic_authentication_header( + proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + } + + if (!bearer_token_auth_token_.empty()) { + if (!req.has_header("Authorization")) { + req.headers.insert(make_bearer_token_authentication_header( + bearer_token_auth_token_, false)); + } + } + + if (!proxy_bearer_token_auth_token_.empty()) { + if (!req.has_header("Proxy-Authorization")) { + req.headers.insert(make_bearer_token_authentication_header( + proxy_bearer_token_auth_token_, true)); + } + } + + // Request line and headers + { + detail::BufferStream bstrm; + + const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + header_writer_(bstrm, req.headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + error = Error::Write; + return false; + } + } + + // Body + if (req.body.empty()) { + return write_content_with_provider(strm, req, error); + } + + if (!detail::write_data(strm, req.body.data(), req.body.size())) { + error = Error::Write; + return false; + } + + return true; +} + +inline std::unique_ptr ClientImpl::send_with_content_provider( + Request &req, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Error &error) { + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { req.set_header("Content-Encoding", "gzip"); } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_ && !content_provider_without_length) { + // TODO: Brotli support + detail::gzip_compressor compressor; + + if (content_provider) { + auto ok = true; + size_t offset = 0; + DataSink data_sink; + + data_sink.write = [&](const char *data, size_t data_len) -> bool { + if (ok) { + auto last = offset + data_len == content_length; + + auto ret = compressor.compress( + data, data_len, last, + [&](const char *compressed_data, size_t compressed_data_len) { + req.body.append(compressed_data, compressed_data_len); + return true; + }); + + if (ret) { + offset += data_len; + } else { + ok = false; + } + } + return ok; + }; + + while (ok && offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + error = Error::Canceled; + return nullptr; + } + } + } else { + if (!compressor.compress(body, content_length, true, + [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + return true; + })) { + error = Error::Compression; + return nullptr; + } + } + } else +#endif + { + if (content_provider) { + req.content_length_ = content_length; + req.content_provider_ = std::move(content_provider); + req.is_chunked_content_provider_ = false; + } else if (content_provider_without_length) { + req.content_length_ = 0; + req.content_provider_ = detail::ContentProviderAdapter( + std::move(content_provider_without_length)); + req.is_chunked_content_provider_ = true; + req.set_header("Transfer-Encoding", "chunked"); + } else { + req.body.assign(body, content_length); + } + } + + auto res = detail::make_unique(); + return send(req, *res, error) ? std::move(res) : nullptr; +} + +inline Result ClientImpl::send_with_content_provider( + const std::string &method, const std::string &path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Progress progress) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; + req.progress = progress; + + auto error = Error::Success; + + auto res = send_with_content_provider( + req, body, content_length, std::move(content_provider), + std::move(content_provider_without_length), content_type, error); + + return Result{std::move(res), error, std::move(req.headers)}; +} + +inline std::string +ClientImpl::adjust_host_string(const std::string &host) const { + if (host.find(':') != std::string::npos) { return "[" + host + "]"; } + return host; +} + +inline bool ClientImpl::process_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + // Send request + if (!write_request(strm, req, close_connection, error)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if (is_ssl()) { + auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1; + if (!is_proxy_enabled) { + char buf[1]; + if (SSL_peek(socket_.ssl, buf, 1) == 0 && + SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) { + error = Error::SSLPeerCouldBeClosed_; + return false; + } + } + } +#endif + + // Receive response and headers + if (!read_response_line(strm, req, res) || + !detail::read_headers(strm, res.headers)) { + error = Error::Read; + return false; + } + + // Body + if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" && + req.method != "CONNECT") { + auto redirect = 300 < res.status && res.status < 400 && follow_location_; + + if (req.response_handler && !redirect) { + if (!req.response_handler(res)) { + error = Error::Canceled; + return false; + } + } + + auto out = + req.content_receiver + ? static_cast( + [&](const char *buf, size_t n, uint64_t off, uint64_t len) { + if (redirect) { return true; } + auto ret = req.content_receiver(buf, n, off, len); + if (!ret) { error = Error::Canceled; } + return ret; + }) + : static_cast( + [&](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; + }); + + auto progress = [&](uint64_t current, uint64_t total) { + if (!req.progress || redirect) { return true; } + auto ret = req.progress(current, total); + if (!ret) { error = Error::Canceled; } + return ret; + }; + + int dummy_status; + if (!detail::read_content(strm, res, (std::numeric_limits::max)(), + dummy_status, std::move(progress), std::move(out), + decompress_)) { + if (error != Error::Canceled) { error = Error::Read; } + return false; + } + } + + // Log + if (logger_) { logger_(req, res); } + + return true; +} + +inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( + const std::string &boundary, const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) const { + size_t cur_item = 0; + size_t cur_start = 0; + // cur_item and cur_start are copied to within the std::function and maintain + // state between successive calls + return [&, cur_item, cur_start](size_t offset, + DataSink &sink) mutable -> bool { + if (!offset && !items.empty()) { + sink.os << detail::serialize_multipart_formdata(items, boundary, false); + return true; + } else if (cur_item < provider_items.size()) { + if (!cur_start) { + const auto &begin = detail::serialize_multipart_formdata_item_begin( + provider_items[cur_item], boundary); + offset += begin.size(); + cur_start = offset; + sink.os << begin; + } + + DataSink cur_sink; + auto has_data = true; + cur_sink.write = sink.write; + cur_sink.done = [&]() { has_data = false; }; + + if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) { + return false; + } + + if (!has_data) { + sink.os << detail::serialize_multipart_formdata_item_end(); + cur_item++; + cur_start = 0; + } + return true; + } else { + sink.os << detail::serialize_multipart_formdata_finish(boundary); + sink.done(); + return true; + } + }; +} + +inline bool +ClientImpl::process_socket(const Socket &socket, + std::function callback) { + return detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, std::move(callback)); +} + +inline bool ClientImpl::is_ssl() const { return false; } + +inline Result ClientImpl::Get(const std::string &path) { + return Get(path, Headers(), Progress()); +} + +inline Result ClientImpl::Get(const std::string &path, Progress progress) { + return Get(path, Headers(), std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers) { + return Get(path, headers, Progress()); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(progress); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Get(const std::string &path, + ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), + std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.progress = std::move(progress); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress) { + if (params.empty()) { return Get(path, headers); } + + std::string path_with_query = append_query_params(path, params); + return Get(path_with_query, headers, std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, params, headers, nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + if (params.empty()) { + return Get(path, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); + } + + std::string path_with_query = append_query_params(path, params); + return Get(path_with_query, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result ClientImpl::Head(const std::string &path) { + return Head(path, Headers()); +} + +inline Result ClientImpl::Head(const std::string &path, + const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + return send_(std::move(req)); +} + +inline Result ClientImpl::Post(const std::string &path) { + return Post(path, std::string(), std::string()); +} + +inline Result ClientImpl::Post(const std::string &path, + const Headers &headers) { + return Post(path, headers, nullptr, 0, std::string()); +} + +inline Result ClientImpl::Post(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Post(path, Headers(), body, content_length, content_type, nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, body, content_length, + nullptr, nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("POST", path, headers, body, content_length, + nullptr, nullptr, content_type, progress); +} + +inline Result ClientImpl::Post(const std::string &path, const std::string &body, + const std::string &content_type) { + return Post(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Post(const std::string &path, const std::string &body, + const std::string &content_type, + Progress progress) { + return Post(path, Headers(), body, content_type, progress); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("POST", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + progress); +} + +inline Result ClientImpl::Post(const std::string &path, const Params ¶ms) { + return Post(path, Headers(), params); +} + +inline Result ClientImpl::Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return Post(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return Post(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type, + nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded", + progress); +} + +inline Result ClientImpl::Post(const std::string &path, + const MultipartFormDataItems &items) { + return Post(path, Headers(), items); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Post(path, headers, body, content_type); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + if (!detail::is_multipart_boundary_chars_valid(boundary)) { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Post(path, headers, body, content_type); +} + +inline Result +ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + return send_with_content_provider( + "POST", path, headers, nullptr, 0, nullptr, + get_multipart_content_provider(boundary, items, provider_items), + content_type, nullptr); +} + +inline Result ClientImpl::Put(const std::string &path) { + return Put(path, std::string(), std::string()); +} + +inline Result ClientImpl::Put(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Put(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, body, content_length, + nullptr, nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PUT", path, headers, body, content_length, + nullptr, nullptr, content_type, progress); +} + +inline Result ClientImpl::Put(const std::string &path, const std::string &body, + const std::string &content_type) { + return Put(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const std::string &body, + const std::string &content_type, + Progress progress) { + return Put(path, Headers(), body, content_type, progress); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PUT", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + progress); +} + +inline Result ClientImpl::Put(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return Put(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return Put(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type, + nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Params ¶ms) { + return Put(path, Headers(), params); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded", + progress); +} + +inline Result ClientImpl::Put(const std::string &path, + const MultipartFormDataItems &items) { + return Put(path, Headers(), items); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Put(path, headers, body, content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + if (!detail::is_multipart_boundary_chars_valid(boundary)) { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Put(path, headers, body, content_type); +} + +inline Result +ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + return send_with_content_provider( + "PUT", path, headers, nullptr, 0, nullptr, + get_multipart_content_provider(boundary, items, provider_items), + content_type, nullptr); +} +inline Result ClientImpl::Patch(const std::string &path) { + return Patch(path, std::string(), std::string()); +} + +inline Result ClientImpl::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Patch(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, + Progress progress) { + return Patch(path, Headers(), body, content_length, content_type, progress); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return Patch(path, headers, body, content_length, content_type, nullptr); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PATCH", path, headers, body, + content_length, nullptr, nullptr, + content_type, progress); +} + +inline Result ClientImpl::Patch(const std::string &path, + const std::string &body, + const std::string &content_type) { + return Patch(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, + const std::string &body, + const std::string &content_type, Progress progress) { + return Patch(path, Headers(), body, content_type, progress); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return Patch(path, headers, body, content_type, nullptr); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PATCH", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + progress); +} + +inline Result ClientImpl::Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return Patch(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return Patch(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type, + nullptr); +} + +inline Result ClientImpl::Delete(const std::string &path) { + return Delete(path, Headers(), std::string(), std::string()); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers) { + return Delete(path, headers, std::string(), std::string()); +} + +inline Result ClientImpl::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Delete(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, + Progress progress) { + return Delete(path, Headers(), body, content_length, content_type, progress); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, const char *body, + size_t content_length, + const std::string &content_type) { + return Delete(path, headers, body, content_length, content_type, nullptr); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, const char *body, + size_t content_length, + const std::string &content_type, + Progress progress) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + req.progress = progress; + + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + req.body.assign(body, content_length); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Delete(const std::string &path, + const std::string &body, + const std::string &content_type) { + return Delete(path, Headers(), body.data(), body.size(), content_type); +} + +inline Result ClientImpl::Delete(const std::string &path, + const std::string &body, + const std::string &content_type, + Progress progress) { + return Delete(path, Headers(), body.data(), body.size(), content_type, + progress); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, + const std::string &body, + const std::string &content_type) { + return Delete(path, headers, body.data(), body.size(), content_type); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return Delete(path, headers, body.data(), body.size(), content_type, + progress); +} + +inline Result ClientImpl::Options(const std::string &path) { + return Options(path, Headers()); +} + +inline Result ClientImpl::Options(const std::string &path, + const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.headers = headers; + req.path = path; + + return send_(std::move(req)); +} + +inline void ClientImpl::stop() { + std::lock_guard guard(socket_mutex_); + + // If there is anything ongoing right now, the ONLY thread-safe thing we can + // do is to shutdown_socket, so that threads using this socket suddenly + // discover they can't read/write any more and error out. Everything else + // (closing the socket, shutting ssl down) is unsafe because these actions are + // not thread-safe. + if (socket_requests_in_flight_ > 0) { + shutdown_socket(socket_); + + // Aside from that, we set a flag for the socket to be closed when we're + // done. + socket_should_be_closed_when_request_is_done_ = true; + return; + } + + // Otherwise, still holding the mutex, we can shut everything down ourselves + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); +} + +inline std::string ClientImpl::host() const { return host_; } + +inline int ClientImpl::port() const { return port_; } + +inline size_t ClientImpl::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); +} + +inline socket_t ClientImpl::socket() const { return socket_.sock; } + +inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; +} + +inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void ClientImpl::set_basic_auth(const std::string &username, + const std::string &password) { + basic_auth_username_ = username; + basic_auth_password_ = password; +} + +inline void ClientImpl::set_bearer_token_auth(const std::string &token) { + bearer_token_auth_token_ = token; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_digest_auth(const std::string &username, + const std::string &password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + +inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } + +inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } + +inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } + +inline void +ClientImpl::set_hostname_addr_map(std::map addr_map) { + addr_map_ = std::move(addr_map); +} + +inline void ClientImpl::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); +} + +inline void ClientImpl::set_header_writer( + std::function const &writer) { + header_writer_ = writer; +} + +inline void ClientImpl::set_address_family(int family) { + address_family_ = family; +} + +inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } + +inline void ClientImpl::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); +} + +inline void ClientImpl::set_compress(bool on) { compress_ = on; } + +inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } + +inline void ClientImpl::set_interface(const std::string &intf) { + interface_ = intf; +} + +inline void ClientImpl::set_proxy(const std::string &host, int port) { + proxy_host_ = host; + proxy_port_ = port; +} + +inline void ClientImpl::set_proxy_basic_auth(const std::string &username, + const std::string &password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; +} + +inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) { + proxy_bearer_token_auth_token_ = token; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_proxy_digest_auth(const std::string &username, + const std::string &password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} + +inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path) { + ca_cert_file_path_ = ca_cert_file_path; + ca_cert_dir_path_ = ca_cert_dir_path; +} + +inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store && ca_cert_store != ca_cert_store_) { + ca_cert_store_ = ca_cert_store; + } +} + +inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, + std::size_t size) const { + auto mem = BIO_new_mem_buf(ca_cert, static_cast(size)); + if (!mem) { return nullptr; } + + auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr); + if (!inf) { + BIO_free_all(mem); + return nullptr; + } + + auto cts = X509_STORE_new(); + if (cts) { + for (auto i = 0; i < static_cast(sk_X509_INFO_num(inf)); i++) { + auto itmp = sk_X509_INFO_value(inf, i); + if (!itmp) { continue; } + + if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); } + if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); } + } + } + + sk_X509_INFO_pop_free(inf, X509_INFO_free); + BIO_free_all(mem); + return cts; +} + +inline void ClientImpl::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} +#endif + +inline void ClientImpl::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + set_nonblocking(sock, true); + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + BIO_set_nbio(bio, 1); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + set_nonblocking(sock, false); + return nullptr; + } + BIO_set_nbio(bio, 0); + set_nonblocking(sock, false); + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, + bool shutdown_gracefully) { + // sometimes we may want to skip this to try to avoid SIGPIPE if we know + // the remote has closed the network connection + // Note that it is not always possible to avoid SIGPIPE, this is merely a + // best-efforts. + if (shutdown_gracefully) { SSL_shutdown(ssl); } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, + U ssl_connect_or_accept, + time_t timeout_sec, + time_t timeout_usec) { + auto res = 0; + while ((res = ssl_connect_or_accept(ssl)) != 1) { + auto err = SSL_get_error(ssl, res); + switch (err) { + case SSL_ERROR_WANT_READ: + if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + case SSL_ERROR_WANT_WRITE: + if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + default: break; + } + return false; + } + return true; +} + +template +inline bool process_server_socket_ssl( + const std::atomic &svr_sock, SSL *ssl, socket_t sock, + size_t keep_alive_max_count, time_t keep_alive_timeout_sec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +template +inline bool +process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +class SSLInit { +public: + SSLInit() { + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + } +}; + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) { + SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); +} + +inline SSLSocketStream::~SSLSocketStream() = default; + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && + is_socket_alive(sock_); +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + auto ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); + auto n = 1000; +#ifdef _WIN32 + while (--n >= 0 && (err == SSL_ERROR_WANT_READ || + (err == SSL_ERROR_SYSCALL && + WSAGetLastError() == WSAETIMEDOUT))) { +#else + while (--n >= 0 && err == SSL_ERROR_WANT_READ) { +#endif + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { + auto handle_size = static_cast( + std::min(size, (std::numeric_limits::max)())); + + auto ret = SSL_write(ssl_, ptr, static_cast(handle_size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); + auto n = 1000; +#ifdef _WIN32 + while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE || + (err == SSL_ERROR_SYSCALL && + WSAGetLastError() == WSAETIMEDOUT))) { +#else + while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) { +#endif + if (is_writable()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ret = SSL_write(ssl_, ptr, static_cast(handle_size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline void SSLSocketStream::get_local_ip_and_port(std::string &ip, + int &port) const { + detail::get_local_ip_and_port(sock_, ip, port); +} + +inline socket_t SSLSocketStream::socket() const { return sock_; } + +static SSLInit sslinit_; + +} // namespace detail + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path, + const char *client_ca_cert_dir_path, + const char *private_key_password) { + ctx_ = SSL_CTX_new(TLS_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); + + if (private_key_password != nullptr && (private_key_password[0] != '\0')) { + SSL_CTX_set_default_passwd_cb_userdata( + ctx_, + reinterpret_cast(const_cast(private_key_password))); + } + + if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != + 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, + client_ca_cert_dir_path); + + SSL_CTX_set_verify( + ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + } + } +} + +inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store) { + ctx_ = SSL_CTX_new(TLS_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); + + if (SSL_CTX_use_certificate(ctx_, cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_store) { + SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); + + SSL_CTX_set_verify( + ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + } + } +} + +inline SSLServer::SSLServer( + const std::function &setup_ssl_ctx_callback) { + ctx_ = SSL_CTX_new(TLS_method()); + if (ctx_) { + if (!setup_ssl_ctx_callback(*ctx_)) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLServer::~SSLServer() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLServer::is_valid() const { return ctx_; } + +inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; } + +inline bool SSLServer::process_and_close_socket(socket_t sock) { + auto ssl = detail::ssl_new( + sock, ctx_, ctx_mutex_, + [&](SSL *ssl2) { + return detail::ssl_connect_or_accept_nonblocking( + sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_); + }, + [](SSL * /*ssl2*/) { return true; }); + + auto ret = false; + if (ssl) { + ret = detail::process_server_socket_ssl( + svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this, ssl](Stream &strm, bool close_connection, + bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + [&](Request &req) { req.ssl = ssl; }); + }); + + // Shutdown gracefully if the result seemed successful, non-gracefully if + // the connection appeared to be closed. + const bool shutdown_gracefully = ret; + detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully); + } + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const std::string &host) + : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) + : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path, + const std::string &private_key_password) + : ClientImpl(host, port, client_cert_path, client_key_path) { + ctx_ = SSL_CTX_new(TLS_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(b, e); + }); + + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (!private_key_password.empty()) { + SSL_CTX_set_default_passwd_cb_userdata( + ctx_, reinterpret_cast( + const_cast(private_key_password.c_str()))); + } + + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), + SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), + SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, + X509 *client_cert, EVP_PKEY *client_key, + const std::string &private_key_password) + : ClientImpl(host, port) { + ctx_ = SSL_CTX_new(TLS_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(b, e); + }); + + if (client_cert != nullptr && client_key != nullptr) { + if (!private_key_password.empty()) { + SSL_CTX_set_default_passwd_cb_userdata( + ctx_, reinterpret_cast( + const_cast(private_key_password.c_str()))); + } + + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { SSL_CTX_free(ctx_); } + // Make sure to shut down SSL since shutdown_ssl will resolve to the + // base function rather than the derived function once we get to the + // base class destructor, and won't free the SSL (causing a leak). + shutdown_ssl_impl(socket_, true); +} + +inline bool SSLClient::is_valid() const { return ctx_; } + +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { + if (ctx_) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) { + // Free memory allocated for old cert and use new store `ca_cert_store` + SSL_CTX_set_cert_store(ctx_, ca_cert_store); + } + } else { + X509_STORE_free(ca_cert_store); + } + } +} + +inline void SSLClient::load_ca_cert_store(const char *ca_cert, + std::size_t size) { + set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size)); +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } + +inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { + return is_valid() && ClientImpl::create_and_connect_socket(socket, error); +} + +// Assumes that socket_mutex_ is locked and that there are no requests in flight +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, + bool &success, Error &error) { + success = true; + Response proxy_res; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, proxy_res, false, error); + })) { + // Thread-safe to close everything because we are assuming there are no + // requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + + if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) { + if (!proxy_digest_auth_username_.empty() && + !proxy_digest_auth_password_.empty()) { + std::map auth; + if (detail::parse_www_authenticate(proxy_res, auth, true)) { + proxy_res = Response(); + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(detail::make_digest_authentication_header( + req3, auth, 1, detail::random_string(10), + proxy_digest_auth_username_, proxy_digest_auth_password_, + true)); + return process_request(strm, req3, proxy_res, false, error); + })) { + // Thread-safe to close everything because we are assuming there are + // no requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + } + } + } + + // If status code is not 200, proxy request is failed. + // Set error to ProxyConnection and return proxy response + // as the response of the request + if (proxy_res.status != StatusCode::OK_200) { + error = Error::ProxyConnection; + res = std::move(proxy_res); + // Thread-safe to close everything because we are assuming there are + // no requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + return false; + } + + return true; +} + +inline bool SSLClient::load_certs() { + auto ret = true; + + std::call_once(initialize_cert_, [&]() { + std::lock_guard guard(ctx_mutex_); + if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), + nullptr)) { + ret = false; + } + } else if (!ca_cert_dir_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, nullptr, + ca_cert_dir_path_.c_str())) { + ret = false; + } + } else { + auto loaded = false; +#ifdef _WIN32 + loaded = + detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#if TARGET_OS_OSX + loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_)); +#endif // TARGET_OS_OSX +#endif // _WIN32 + if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); } + } + }); + + return ret; +} + +inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { + auto ssl = detail::ssl_new( + socket.sock, ctx_, ctx_mutex_, + [&](SSL *ssl2) { + if (server_certificate_verification_) { + if (!load_certs()) { + error = Error::SSLLoadingCerts; + return false; + } + SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr); + } + + if (!detail::ssl_connect_or_accept_nonblocking( + socket.sock, ssl2, SSL_connect, connection_timeout_sec_, + connection_timeout_usec_)) { + error = Error::SSLConnection; + return false; + } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl2); + + if (verify_result_ != X509_V_OK) { + error = Error::SSLServerVerification; + return false; + } + + auto server_cert = SSL_get1_peer_certificate(ssl2); + + if (server_cert == nullptr) { + error = Error::SSLServerVerification; + return false; + } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + error = Error::SSLServerVerification; + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl2) { + // NOTE: Direct call instead of using the OpenSSL macro to suppress + // -Wold-style-cast warning + // SSL_set_tlsext_host_name(ssl2, host_.c_str()); + SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, + static_cast(const_cast(host_.c_str()))); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + shutdown_socket(socket); + close_socket(socket); + return false; +} + +inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { + shutdown_ssl_impl(socket, shutdown_gracefully); +} + +inline void SSLClient::shutdown_ssl_impl(Socket &socket, + bool shutdown_gracefully) { + if (socket.sock == INVALID_SOCKET) { + assert(socket.ssl == nullptr); + return; + } + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully); + socket.ssl = nullptr; + } + assert(socket.ssl == nullptr); +} + +inline bool +SSLClient::process_socket(const Socket &socket, + std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl( + socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, std::move(callback)); +} + +inline bool SSLClient::is_ssl() const { return true; } + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || + verify_host_with_common_name(server_cert); +} + +inline bool +SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6 {}; + struct in_addr addr {}; + size_t addr_len = 0; + +#ifndef __MINGW32__ + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } +#endif + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_matched = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = + reinterpret_cast(ASN1_STRING_get0_data(val->d.ia5)); + auto name_len = static_cast(ASN1_STRING_length(val->d.ia5)); + + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_matched = true; + } + break; + } + } + } + + if (dsn_matched || ip_matched) { ret = true; } + } + + GENERAL_NAMES_free(const_cast( + reinterpret_cast(alt_names))); + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, + size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { return true; } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', + [&](const char *b, const char *e) { + pattern_components.emplace_back(b, e); + }); + + if (host_components_.size() != pattern_components.size()) { return false; } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && + !p.compare(0, p.size() - 1, h)); + if (!partial_match) { return false; } + } + ++itr; + } + + return true; +} +#endif + +// Universal client implementation +inline Client::Client(const std::string &scheme_host_port) + : Client(scheme_host_port, std::string(), std::string()) {} + +inline Client::Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re( + R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); + + std::smatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if (!scheme.empty() && (scheme != "http" && scheme != "https")) { +#else + if (!scheme.empty() && scheme != "http") { +#endif +#ifndef CPPHTTPLIB_NO_EXCEPTIONS + std::string msg = "'" + scheme + "' scheme is not supported."; + throw std::invalid_argument(msg); +#endif + return; + } + + auto is_ssl = scheme == "https"; + + auto host = m[2].str(); + if (host.empty()) { host = m[3].str(); } + + auto port_str = m[4].str(); + auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); + + if (is_ssl) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_ = detail::make_unique(host, port, client_cert_path, + client_key_path); + is_ssl_ = is_ssl; +#endif + } else { + cli_ = detail::make_unique(host, port, client_cert_path, + client_key_path); + } + } else { + // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress) + // if port param below changes. + cli_ = detail::make_unique(scheme_host_port, 80, + client_cert_path, client_key_path); + } +} + +inline Client::Client(const std::string &host, int port) + : cli_(detail::make_unique(host, port)) {} + +inline Client::Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : cli_(detail::make_unique(host, port, client_cert_path, + client_key_path)) {} + +inline Client::~Client() = default; + +inline bool Client::is_valid() const { + return cli_ != nullptr && cli_->is_valid(); +} + +inline Result Client::Get(const std::string &path) { return cli_->Get(path); } +inline Result Client::Get(const std::string &path, const Headers &headers) { + return cli_->Get(path, headers); +} +inline Result Client::Get(const std::string &path, Progress progress) { + return cli_->Get(path, std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + Progress progress) { + return cli_->Get(path, headers, std::move(progress)); +} +inline Result Client::Get(const std::string &path, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(content_receiver), + std::move(progress)); +} +inline Result Client::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress) { + return cli_->Get(path, params, headers, std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, std::move(content_receiver), + std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result Client::Head(const std::string &path) { return cli_->Head(path); } +inline Result Client::Head(const std::string &path, const Headers &headers) { + return cli_->Head(path, headers); +} + +inline Result Client::Post(const std::string &path) { return cli_->Post(path); } +inline Result Client::Post(const std::string &path, const Headers &headers) { + return cli_->Post(path, headers); +} +inline Result Client::Post(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Post(path, body, content_length, content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Post(path, headers, body, content_length, content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Post(path, headers, body, content_length, content_type, + progress); +} +inline Result Client::Post(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Post(path, body, content_type); +} +inline Result Client::Post(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Post(path, body, content_type, progress); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Post(path, headers, body, content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Post(path, headers, body, content_type, progress); +} +inline Result Client::Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Post(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Post(path, std::move(content_provider), content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Post(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Post(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Post(const std::string &path, const Params ¶ms) { + return cli_->Post(path, params); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const Params ¶ms) { + return cli_->Post(path, headers, params); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + return cli_->Post(path, headers, params, progress); +} +inline Result Client::Post(const std::string &path, + const MultipartFormDataItems &items) { + return cli_->Post(path, items); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Post(path, headers, items, boundary); +} +inline Result +Client::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + return cli_->Post(path, headers, items, provider_items); +} +inline Result Client::Put(const std::string &path) { return cli_->Put(path); } +inline Result Client::Put(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Put(path, body, content_length, content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Put(path, headers, body, content_length, content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Put(path, headers, body, content_length, content_type, progress); +} +inline Result Client::Put(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Put(path, body, content_type); +} +inline Result Client::Put(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Put(path, body, content_type, progress); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Put(path, headers, body, content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Put(path, headers, body, content_type, progress); +} +inline Result Client::Put(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Put(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Put(path, std::move(content_provider), content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Put(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Put(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Put(const std::string &path, const Params ¶ms) { + return cli_->Put(path, params); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const Params ¶ms) { + return cli_->Put(path, headers, params); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + return cli_->Put(path, headers, params, progress); +} +inline Result Client::Put(const std::string &path, + const MultipartFormDataItems &items) { + return cli_->Put(path, items); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Put(path, headers, items); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Put(path, headers, items, boundary); +} +inline Result +Client::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + return cli_->Put(path, headers, items, provider_items); +} +inline Result Client::Patch(const std::string &path) { + return cli_->Patch(path); +} +inline Result Client::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Patch(path, body, content_length, content_type); +} +inline Result Client::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, body, content_length, content_type, progress); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Patch(path, headers, body, content_length, content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, headers, body, content_length, content_type, progress); +} +inline Result Client::Patch(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Patch(path, body, content_type); +} +inline Result Client::Patch(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, body, content_type, progress); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Patch(path, headers, body, content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, headers, body, content_type, progress); +} +inline Result Client::Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Patch(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Patch(path, std::move(content_provider), content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Patch(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Patch(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Delete(const std::string &path) { + return cli_->Delete(path); +} +inline Result Client::Delete(const std::string &path, const Headers &headers) { + return cli_->Delete(path, headers); +} +inline Result Client::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Delete(path, body, content_length, content_type); +} +inline Result Client::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, body, content_length, content_type, progress); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Delete(path, headers, body, content_length, content_type); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, headers, body, content_length, content_type, progress); +} +inline Result Client::Delete(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Delete(path, body, content_type); +} +inline Result Client::Delete(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, body, content_type, progress); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Delete(path, headers, body, content_type); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, headers, body, content_type, progress); +} +inline Result Client::Options(const std::string &path) { + return cli_->Options(path); +} +inline Result Client::Options(const std::string &path, const Headers &headers) { + return cli_->Options(path, headers); +} + +inline bool Client::send(Request &req, Response &res, Error &error) { + return cli_->send(req, res, error); +} + +inline Result Client::send(const Request &req) { return cli_->send(req); } + +inline void Client::stop() { cli_->stop(); } + +inline std::string Client::host() const { return cli_->host(); } + +inline int Client::port() const { return cli_->port(); } + +inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } + +inline socket_t Client::socket() const { return cli_->socket(); } + +inline void +Client::set_hostname_addr_map(std::map addr_map) { + cli_->set_hostname_addr_map(std::move(addr_map)); +} + +inline void Client::set_default_headers(Headers headers) { + cli_->set_default_headers(std::move(headers)); +} + +inline void Client::set_header_writer( + std::function const &writer) { + cli_->set_header_writer(writer); +} + +inline void Client::set_address_family(int family) { + cli_->set_address_family(family); +} + +inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } + +inline void Client::set_socket_options(SocketOptions socket_options) { + cli_->set_socket_options(std::move(socket_options)); +} + +inline void Client::set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); +} + +inline void Client::set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); +} + +inline void Client::set_write_timeout(time_t sec, time_t usec) { + cli_->set_write_timeout(sec, usec); +} + +inline void Client::set_basic_auth(const std::string &username, + const std::string &password) { + cli_->set_basic_auth(username, password); +} +inline void Client::set_bearer_token_auth(const std::string &token) { + cli_->set_bearer_token_auth(token); +} +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_digest_auth(const std::string &username, + const std::string &password) { + cli_->set_digest_auth(username, password); +} +#endif + +inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } +inline void Client::set_follow_location(bool on) { + cli_->set_follow_location(on); +} + +inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } + +inline void Client::set_compress(bool on) { cli_->set_compress(on); } + +inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } + +inline void Client::set_interface(const std::string &intf) { + cli_->set_interface(intf); +} + +inline void Client::set_proxy(const std::string &host, int port) { + cli_->set_proxy(host, port); +} +inline void Client::set_proxy_basic_auth(const std::string &username, + const std::string &password) { + cli_->set_proxy_basic_auth(username, password); +} +inline void Client::set_proxy_bearer_token_auth(const std::string &token) { + cli_->set_proxy_bearer_token_auth(token); +} +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_proxy_digest_auth(const std::string &username, + const std::string &password) { + cli_->set_proxy_digest_auth(username, password); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::enable_server_certificate_verification(bool enabled) { + cli_->enable_server_certificate_verification(enabled); +} +#endif + +inline void Client::set_logger(Logger logger) { + cli_->set_logger(std::move(logger)); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path) { + cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); +} + +inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } else { + cli_->set_ca_cert_store(ca_cert_store); + } +} + +inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) { + set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size)); +} + +inline long Client::get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? +} + +inline SSL_CTX *Client::ssl_context() const { + if (is_ssl_) { return static_cast(*cli_).ssl_context(); } + return nullptr; +} +#endif + +// ---------------------------------------------------------------------------- + +} // namespace httplib + +#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL) +#undef poll +#endif + +#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/meson.build b/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..e82ae84514ba675445fb42aa08dda4a6bb89de45 --- /dev/null +++ b/meson.build @@ -0,0 +1,118 @@ +# SPDX-FileCopyrightText: 2021 Andrea Pappacoda +# +# SPDX-License-Identifier: MIT + +project( + 'cpp-httplib', + 'cpp', + license: 'MIT', + default_options: [ + 'cpp_std=c++11', + 'buildtype=release', + 'b_ndebug=if-release', + 'b_lto=true', + 'warning_level=3' + ], + meson_version: '>=0.47.0' +) + +# Check just in case downstream decides to edit the source +# and add a project version +version = meson.project_version() +if version == 'undefined' + cxx = meson.get_compiler('cpp') + version = cxx.get_define('CPPHTTPLIB_VERSION', + prefix: '#include ', + include_directories: include_directories('.')).strip('"') + assert(version != '', 'failed to get version from httplib.h') +endif + +deps = [dependency('threads')] +args = [] + +openssl_dep = dependency('openssl', version: '>=3.0.0', required: get_option('cpp-httplib_openssl')) +if openssl_dep.found() + deps += openssl_dep + args += '-DCPPHTTPLIB_OPENSSL_SUPPORT' + if host_machine.system() == 'darwin' + macosx_keychain_dep = dependency('appleframeworks', modules: ['CoreFoundation', 'Security'], required: get_option('cpp-httplib_macosx_keychain')) + if macosx_keychain_dep.found() + deps += macosx_keychain_dep + args += '-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN' + endif + endif +endif + +zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib')) +if zlib_dep.found() + deps += zlib_dep + args += '-DCPPHTTPLIB_ZLIB_SUPPORT' +endif + +brotli_deps = [dependency('libbrotlicommon', required: get_option('cpp-httplib_brotli'))] +brotli_deps += dependency('libbrotlidec', required: get_option('cpp-httplib_brotli')) +brotli_deps += dependency('libbrotlienc', required: get_option('cpp-httplib_brotli')) + +brotli_found_all = true +foreach brotli_dep : brotli_deps + if not brotli_dep.found() + brotli_found_all = false + endif +endforeach + +if brotli_found_all + deps += brotli_deps + args += '-DCPPHTTPLIB_BROTLI_SUPPORT' +endif + +cpp_httplib_dep = dependency('', required: false) + +if get_option('cpp-httplib_compile') + python3 = find_program('python3') + + httplib_ch = custom_target( + 'split', + input: 'httplib.h', + output: ['httplib.cc', 'httplib.h'], + command: [python3, files('split.py'), '--out', meson.current_build_dir()], + install: true, + install_dir: [false, get_option('includedir')] + ) + lib = library( + 'cpp-httplib', + sources: httplib_ch, + dependencies: deps, + cpp_args: args, + version: version, + soversion: version.split('.')[0] + '.' + version.split('.')[1], + install: true + ) + cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1]) + + import('pkgconfig').generate( + lib, + description: 'A C++ HTTP/HTTPS server and client library', + extra_cflags: args, + url: 'https://github.com/yhirose/cpp-httplib', + version: version + ) +else + install_headers('httplib.h') + cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: include_directories('.')) + + import('pkgconfig').generate( + name: 'cpp-httplib', + description: 'A C++ HTTP/HTTPS server and client library', + install_dir: join_paths(get_option('datadir'), 'pkgconfig'), + url: 'https://github.com/yhirose/cpp-httplib', + version: version + ) +endif + +if meson.version().version_compare('>=0.54.0') + meson.override_dependency('cpp-httplib', cpp_httplib_dep) +endif + +if get_option('cpp-httplib_test') + subdir('test') +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000000000000000000000000000000000000..e15847d42f7814632c8f07f848eaafc1c9c37ca8 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2021 Andrea Pappacoda +# +# SPDX-License-Identifier: MIT + +option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support') +option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support') +option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support') +option('cpp-httplib_macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices') +option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)') +option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests') diff --git a/split.py b/split.py new file mode 100644 index 0000000000000000000000000000000000000000..4d8b3074170b0b3f8a218dda2313d978ad7bd664 --- /dev/null +++ b/split.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +"""This script splits httplib.h into .h and .cc parts.""" + +import argparse +import os +import sys + +border = '// ----------------------------------------------------------------------------' + +args_parser = argparse.ArgumentParser(description=__doc__) +args_parser.add_argument( + "-e", "--extension", help="extension of the implementation file (default: cc)", + default="cc" +) +args_parser.add_argument( + "-o", "--out", help="where to write the files (default: out)", default="out" +) +args = args_parser.parse_args() + +cur_dir = os.path.dirname(sys.argv[0]) +lib_name = 'httplib' +header_name = '/' + lib_name + '.h' +source_name = '/' + lib_name + '.' + args.extension +# get the input file +in_file = cur_dir + header_name +# get the output file +h_out = args.out + header_name +cc_out = args.out + source_name + +# if the modification time of the out file is after the in file, +# don't split (as it is already finished) +do_split = True + +if os.path.exists(h_out): + in_time = os.path.getmtime(in_file) + out_time = os.path.getmtime(h_out) + do_split = in_time > out_time + +if do_split: + with open(in_file) as f: + lines = f.readlines() + + python_version = sys.version_info[0] + if python_version < 3: + os.makedirs(args.out) + else: + os.makedirs(args.out, exist_ok=True) + + in_implementation = False + cc_out = args.out + source_name + with open(h_out, 'w') as fh, open(cc_out, 'w') as fc: + fc.write('#include "httplib.h"\n') + fc.write('namespace httplib {\n') + for line in lines: + is_border_line = border in line + if is_border_line: + in_implementation = not in_implementation + elif in_implementation: + fc.write(line.replace('inline ', '')) + else: + fh.write(line) + fc.write('} // namespace httplib\n') + + print("Wrote {} and {}".format(h_out, cc_out)) +else: + print("{} and {} are up to date".format(h_out, cc_out)) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d98225331767dd135f5a0f3bb0090dd69838ac5b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,114 @@ +find_package(GTest) + +if(GTest_FOUND) + if(NOT TARGET GTest::gtest_main AND TARGET GTest::Main) + # CMake <3.20 + add_library(GTest::gtest_main INTERFACE IMPORTED) + target_link_libraries(GTest::gtest_main INTERFACE GTest::Main) + endif() +else() + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + endif() + + include(FetchContent) + + set(BUILD_GMOCK OFF) + set(INSTALL_GTEST OFF) + set(gtest_force_shared_crt ON) + + FetchContent_Declare( + gtest + URL https://github.com/google/googletest/archive/main.tar.gz + ) + FetchContent_MakeAvailable(gtest) +endif() + +add_executable(httplib-test test.cc) +target_compile_options(httplib-test PRIVATE "$<$:/utf-8;/bigobj>") +target_link_libraries(httplib-test PRIVATE httplib GTest::gtest_main) +gtest_discover_tests(httplib-test) + +file( + COPY www www2 www3 ca-bundle.crt image.jpg + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} +) + +if(HTTPLIB_IS_USING_OPENSSL) + find_program(OPENSSL_COMMAND + NAMES openssl + PATHS ${OPENSSL_INCLUDE_DIR}/../bin + REQUIRED + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} genrsa 2048 + OUTPUT_FILE key.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem + COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key.pem + OUTPUT_FILE cert.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} req -x509 -new -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} genrsa 2048 + OUTPUT_FILE rootCA.key.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} req -x509 -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.rootCA.conf -key rootCA.key.pem -days 1024 + OUTPUT_FILE rootCA.cert.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} genrsa 2048 + OUTPUT_FILE client.key.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client.key.pem + COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial + OUTPUT_FILE client.cert.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} genrsa -passout pass:test123! 2048 + OUTPUT_FILE key_encrypted.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key_encrypted.pem + COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key_encrypted.pem + OUTPUT_FILE cert_encrypted.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} genrsa -aes256 -passout pass:test012! 2048 + OUTPUT_FILE client_encrypted.key.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client_encrypted.key.pem -passin pass:test012! + COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial + OUTPUT_FILE client_encrypted.cert.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) +endif() + +add_subdirectory(fuzzing) diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5468488e4f4f557360d8e3f9123c9dae8b765383 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,80 @@ +CXX = clang++ +CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address + +PREFIX ?= $(shell brew --prefix) + +OPENSSL_DIR = $(PREFIX)/opt/openssl@3 +OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto + +ifneq ($(OS), Windows_NT) + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S), Darwin) + OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework CoreFoundation -framework Security + endif +endif + +ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz + +BROTLI_DIR = $(PREFIX)/opt/brotli +BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec + +TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread + +# By default, use standalone_fuzz_target_runner. +# This runner does no fuzzing, but simply executes the inputs +# provided via parameters. +# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a" +# to link the fuzzer(s) against a real fuzzing engine. +# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE. +LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o + +all : test test_split + ./test + +proxy : test_proxy + ./test_proxy + +test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem + $(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS) + +# Note: The intention of test_split is to verify that it works to compile and +# link the split httplib.h, so there is normally no need to execute it. +test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem + $(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS) + +test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem + $(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS) + +# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE). +# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer +fuzz_test: server_fuzzer + ./server_fuzzer fuzzing/corpus/* + +# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use. +server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o + $(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread + +# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and +# feeds it to server_fuzzer. +standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp + $(CXX) -o $@ -I.. $(CXXFLAGS) -c $< + +httplib.cc : ../httplib.h + python3 ../split.py -o . + +cert.pem: + openssl genrsa 2048 > key.pem + openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem + openssl req -x509 -config test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN + openssl genrsa 2048 > rootCA.key.pem + openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem + openssl genrsa 2048 > client.key.pem + openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem + openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem + openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem + openssl genrsa -aes256 -passout pass:test012! 2048 > client_encrypted.key.pem + openssl req -new -batch -config test.conf -key client_encrypted.key.pem -passin pass:test012! | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem + #c_rehash . + +clean: + rm -f test test_split test_proxy server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc diff --git a/test/ca-bundle.crt b/test/ca-bundle.crt new file mode 100644 index 0000000000000000000000000000000000000000..7d61eb5cf1b0e6f0fbaa6b3585cc3d17c1009334 --- /dev/null +++ b/test/ca-bundle.crt @@ -0,0 +1,3401 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Jan 22 14:14:40 2019 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.27. +## SHA256: 18372117493b5b7ec006c31d966143fc95a9464a2b5f8d5188e23c5557b2292d +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) FÅ‘tanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ +5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn +vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj +CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil +e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR +OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI +CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 +48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi +trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 +qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB +AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC +ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA +A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz ++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj +f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN +kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk +CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF +URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb +CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h +oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV +IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm +66+KAQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl +OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV +MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF +JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G3 +================================== +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y +olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t +x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy +EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K +Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur +mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5 +1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp +07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo +FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE +41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu +yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq +KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1 +v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA +8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b +8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r +mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq +1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI +JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV +tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk= +-----END CERTIFICATE----- + +Staat der Nederlanden EV Root CA +================================ +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M +MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl +cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk +SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW +O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r +0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 +Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV +XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr +08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV +0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd +74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx +fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa +ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu +c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq +5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN +b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN +f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi +5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 +WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK +DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy +eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +Certinomis - Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg +LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx +EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD +ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos +P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo +d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap +z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00 +8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x +RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE +6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t +FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV +PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH +i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj +YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I +6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV +WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw +Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX +lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ +y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9 +Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng +DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi +I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM +cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr +hkIGuUE= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +LuxTrust Global Root 2 +====================== +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG +A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh +bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW +MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm +Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2 +xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC +wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm +1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm +FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF +wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/ +a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U +ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ +MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB +/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5 +Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ +FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN +H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW +7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu +ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA +VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR +TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt +/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc +7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I +iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +GDCA TrustAUTH R5 ROOT +====================== +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw +BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD +DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow +YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs +AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p +OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr +pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ +9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ +xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM +R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ +D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 +oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx +9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 +H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 +6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd ++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ +HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD +F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ +8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv +/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT +aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +TrustCor RootCert CA-1 +====================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx +MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu +YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe +VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy +dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq +jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 +pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 +JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h +gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw +/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j +BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 +mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C +qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P +3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +TrustCor RootCert CA-2 +====================== +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w +DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT +eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 +eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy +MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h +bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 +IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb +ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk +RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 +oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb +XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 +/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q +jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP +eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg +rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU +2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h +Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp +kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv +2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 +S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw +PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv +DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU +RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE +xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX +RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ +-----END CERTIFICATE----- + +TrustCor ECA-1 +============== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw +N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 +MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y +IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR +MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 +xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc +p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ +fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj +YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL +f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u +/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs +J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC +jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== +-----END CERTIFICATE----- + +SSL.com Root Certification Authority RSA +======================================== +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM +BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x +MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw +MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM +LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C +Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 +P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge +oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp +k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z +fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ +gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 +UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 +1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s +bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr +dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf +ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl +u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq +erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj +MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ +vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI +Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y +wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI +WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +SSL.com Root Certification Authority ECC +======================================== +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv +BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy +MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO +BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ +8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR +hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT +jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW +e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z +5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority RSA R2 +============================================== +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w +DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u +MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD +VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh +hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w +cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO +Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ +B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh +CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim +9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto +RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm +JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 ++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp +qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 +++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx +Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G +guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz +OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 +CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq +lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR +rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 +hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX +9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority ECC +=========================================== +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy +BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw +MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM +LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy +3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O +BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe +5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ +N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm +m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx +9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r +aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW +r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM +LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly +4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr +06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om +3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu +JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM +BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv +fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm +ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b +gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq +4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr +tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo +pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0 +sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql +CFF1pkgl +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk +k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo +7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI +m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm +dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu +ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz +cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl +aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy +5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM +BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ ++YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw +c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da +WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r +n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu +Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ +7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs +gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld +o/DUhgkC +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU +Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP +0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0 +glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa +KaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa +6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV +2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI +N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x +zPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- diff --git a/test/fuzzing/CMakeLists.txt b/test/fuzzing/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7e416c7eb106963ab406b5be95d91661505df479 --- /dev/null +++ b/test/fuzzing/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB HTTPLIB_CORPUS corpus/*) +add_executable(httplib-test-fuzz + server_fuzzer.cc + standalone_fuzz_target_runner.cpp +) +target_link_libraries(httplib-test-fuzz PRIVATE httplib) +add_test( + NAME httplib-test-fuzz + COMMAND httplib-test-fuzz ${HTTPLIB_CORPUS} +) diff --git a/test/fuzzing/Makefile b/test/fuzzing/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d6a3e21bc10a7cef9366ccb9c93ddf7c9d4efcca --- /dev/null +++ b/test/fuzzing/Makefile @@ -0,0 +1,27 @@ + +#CXX = clang++ +# Do not add default sanitizer flags here as OSS-fuzz adds its own sanitizer flags. +CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I../.. -I. -Wall -Wextra -Wtype-limits -Wconversion + +OPENSSL_DIR = /usr/local/opt/openssl@1.1 + +# Using full path to libssl and libcrypto to avoid accidentally picking openssl libs brought in by msan. +OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -I$(OPENSSL_DIR)/lib /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a + +ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz + +BROTLI_DIR = /usr/local/opt/brotli +# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec + +# Runs all the tests and also fuzz tests against seed corpus. +all : server_fuzzer + ./server_fuzzer corpus/* + +# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use. +server_fuzzer : server_fuzzer.cc ../../httplib.h +# $(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread + $(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread + zip -q -r server_fuzzer_seed_corpus.zip corpus + +clean: + rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip diff --git a/test/fuzzing/corpus/1 b/test/fuzzing/corpus/1 new file mode 100644 index 0000000000000000000000000000000000000000..2b9fcc44d4242caf3bcc8165d5c8ca62755914d5 --- /dev/null +++ b/test/fuzzing/corpus/1 @@ -0,0 +1 @@ +PUT /search/sample?a=12 HTTP/1.1 \ No newline at end of file diff --git a/test/fuzzing/corpus/2 b/test/fuzzing/corpus/2 new file mode 100644 index 0000000000000000000000000000000000000000..bdb9bccecc1a5e41250832004ebafa7acad0c967 --- /dev/null +++ b/test/fuzzing/corpus/2 @@ -0,0 +1,5 @@ +GET /hello.htm HTTP/1.1 +User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) +Accept-Language: en-us +Accept-Encoding: gzip, deflate +Connection: Keep-Alive \ No newline at end of file diff --git a/test/fuzzing/corpus/3 b/test/fuzzing/corpus/3 new file mode 100644 index 0000000000000000000000000000000000000000..878944f52b023cb0b609e3f5173b361d6ab96c67 Binary files /dev/null and b/test/fuzzing/corpus/3 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5042094968537088 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5042094968537088 new file mode 100644 index 0000000000000000000000000000000000000000..0325729036eeef76902e138b0f0edad8fe884429 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5042094968537088 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5372331946541056 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5372331946541056 new file mode 100644 index 0000000000000000000000000000000000000000..6fca86b02dcbde747d972241990aa90ab281b7d4 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5372331946541056 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5386708825800704 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5386708825800704 new file mode 100644 index 0000000000000000000000000000000000000000..1f1e4aeb8c04180ccd014f54b6e9f48e8054c712 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5386708825800704 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5667822731132928 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5667822731132928 new file mode 100644 index 0000000000000000000000000000000000000000..b21d1cee9db6fcfd60f780ce92b1c46bed591a41 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5667822731132928 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5886572146327552 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5886572146327552 new file mode 100644 index 0000000000000000000000000000000000000000..797165c50b2ce9fd62fc71f5ca322aeac8ac3bf4 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5886572146327552 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5942767436562432 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5942767436562432 new file mode 100644 index 0000000000000000000000000000000000000000..a2fedd59cf1bf07854832fbff53d2c2b4f1d7a09 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-5942767436562432 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-6007379124158464 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-6007379124158464 new file mode 100644 index 0000000000000000000000000000000000000000..4c4c57e819e60fbc67e1ecc8a554ad13112ba739 Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-6007379124158464 differ diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-6508706672541696 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-6508706672541696 new file mode 100644 index 0000000000000000000000000000000000000000..6f89836414c1a021fb87286bc2b5635c72e22d75 --- /dev/null +++ b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-server_fuzzer-6508706672541696 @@ -0,0 +1,7 @@ +PUT { HTTP/1.0 +Content-Type:multipart/form-databoundary=m +Range:bytes=- + +--m +C +c PUT Ñ?&+&:&<&ã&I&`&a&Ó&s&&Æ&2&ä&Å&@&!& ï ‮ ‌ PUT Ñ?&+&:&<&ã&I&`&a&Ó&s&&Æ&2&ä&Å&@&!& PUT Ñ?&+&:&<&ã&I&`&a&Ó&s&&Æ&2&ä&Å&@&!& ¹ X-Forwarded-Host ‮ ‌ ¹ ¹ X-Forwarded-Host ï ‮ ‌ PUT Ñ?&+&:&<&ã&I&`&a&Ó&s&&Æ&2&ä&Å&@&!& PUT Ñ?&+&:&<&ã&I&`&a&Ó&s&&Æ&2&ä&Å&@&!& ¹ X-Forwarded-Host ‮ ‌ ¹ 2 +/v+ \ No newline at end of file diff --git a/test/fuzzing/corpus/issue1264 b/test/fuzzing/corpus/issue1264 new file mode 100644 index 0000000000000000000000000000000000000000..fd53db5c9602a82ee8da6ed63ce5dc0e4fd76fcf --- /dev/null +++ b/test/fuzzing/corpus/issue1264 @@ -0,0 +1,19 @@ +POST /fform%u008anoÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔãm%u08ag HTTP/1.0 +DondntGnt-Encodin€z-daExpi%20-Enëv2PUT@ËHTkP/ +Rcnðÿÿÿÿÿÿ ,Cotent-Security-Pz-tes=T“peont.eµ-Typ ènt-Ty@n/***ww-form-urlenc…Tÿû?aLO%KSi@FrÇTTP/1.0 +Cofffffffffffffffffffffffntemt + + + + + +Content-Length:dent:applica;tion/x-wsw-form%`00368aogrlencod368angrlencoded +JJ` +o + +8ÿõContent-EncodxNg:deflatePtipfo + +8 +9Ö2H2  ncod368anPOST # HTTP/1.0 Content-Encoding:defPOST / HTTP/1.0 +Content-Encoding:PUT { HTTP/1.0 +Content-Type:Range:bytes=- multipart/ \ No newline at end of file diff --git a/test/fuzzing/server_fuzzer.cc b/test/fuzzing/server_fuzzer.cc new file mode 100644 index 0000000000000000000000000000000000000000..3c0e948026731fa7e2b87ee41549d025483787b5 --- /dev/null +++ b/test/fuzzing/server_fuzzer.cc @@ -0,0 +1,92 @@ +#include + +#include + +class FuzzedStream : public httplib::Stream { +public: + FuzzedStream(const uint8_t *data, size_t size) + : data_(data), size_(size), read_pos_(0) {} + + ssize_t read(char *ptr, size_t size) override { + if (size + read_pos_ > size_) { size = size_ - read_pos_; } + memcpy(ptr, data_ + read_pos_, size); + read_pos_ += size; + return static_cast(size); + } + + ssize_t write(const char *ptr, size_t size) override { + response_.append(ptr, size); + return static_cast(size); + } + + ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); } + + ssize_t write(const std::string &s) { return write(s.data(), s.size()); } + + bool is_readable() const override { return true; } + + bool is_writable() const override { return true; } + + void get_remote_ip_and_port(std::string &ip, int &port) const override { + ip = "127.0.0.1"; + port = 8080; + } + + void get_local_ip_and_port(std::string &ip, int &port) const override { + ip = "127.0.0.1"; + port = 8080; + } + + socket_t socket() const override { return 0; } + +private: + const uint8_t *data_; + size_t size_; + size_t read_pos_; + std::string response_; +}; + +class FuzzableServer : public httplib::Server { +public: + void ProcessFuzzedRequest(FuzzedStream &stream) { + bool connection_close = false; + process_request(stream, /*last_connection=*/false, connection_close, + nullptr); + } +}; + +static FuzzableServer g_server; + +extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) { + g_server.Get(R"(.*)", + [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content("response content", "text/plain"); + }); + g_server.Post(R"(.*)", + [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content("response content", "text/plain"); + }); + g_server.Put(R"(.*)", + [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content("response content", "text/plain"); + }); + g_server.Patch(R"(.*)", + [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content("response content", "text/plain"); + }); + g_server.Delete( + R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content("response content", "text/plain"); + }); + g_server.Options( + R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content("response content", "text/plain"); + }); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + FuzzedStream stream{data, size}; + g_server.ProcessFuzzedRequest(stream); + return 0; +} diff --git a/test/fuzzing/server_fuzzer.dict b/test/fuzzing/server_fuzzer.dict new file mode 100644 index 0000000000000000000000000000000000000000..47283dc38f264f04d28e46efa16353aaa7befd76 --- /dev/null +++ b/test/fuzzing/server_fuzzer.dict @@ -0,0 +1,224 @@ +# Sources: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields + +# misc +"HTTP/1.1" + +# verbs +"CONNECT" +"DELETE" +"GET" +"HEAD" +"OPTIONS" +"PATCH" +"POST" +"PUT" +"TRACE" + + +# Webdav/caldav verbs +"ACL" +"BASELINE-CONTROL" +"BIND" +"CHECKIN" +"CHECKOUT" +"COPY" +"LABEL" +"LINK" +"LOCK" +"MERGE" +"MKACTIVITY" +"MKCALENDAR" +"MKCOL" +"MKREDIRECTREF" +"MKWORKSPACE" +"MOVE" +"ORDERPATCH" +"PRI" +"PROPFIND" +"PROPPATCH" +"REBIND" +"REPORT" +"SEARCH" +"UNBIND" +"UNCHECKOUT" +"UNLINK" +"UNLOCK" +"UPDATE" +"UPDATEREDIRECTREF" +"VERSION-CONTROL" + + +# Fields +"A-IM" +"Accept" +"Accept-Charset" +"Accept-Datetime" +"Accept-Encoding" +"Accept-Language" +"Accept-Patch" +"Accept-Ranges" +"Access-Control-Allow-Credentials" +"Access-Control-Allow-Headers" +"Access-Control-Allow-Methods" +"Access-Control-Allow-Origin" +"Access-Control-Expose-Headers" +"Access-Control-Max-Age" +"Access-Control-Request-Headers" +"Access-Control-Request-Method" +"Age" +"Allow" +"Alt-Svc" +"Authorization" +"Cache-Control" +"Connection" +"Connection:" +"Content-Disposition" +"Content-Encoding" +"Content-Language" +"Content-Length" +"Content-Location" +"Content-MD5" +"Content-Range" +"Content-Security-Policy" +"Content-Type" +"Cookie" +"DNT" +"Date" +"Delta-Base" +"ETag" +"Expect" +"Expires" +"Forwarded" +"From" +"Front-End-Https" +"HTTP2-Settings" +"Host" +"IM" +"If-Match" +"If-Modified-Since" +"If-None-Match" +"If-Range" +"If-Unmodified-Since" +"Last-Modified" +"Link" +"Location" +"Max-Forwards" +"Origin" +"P3P" +"Pragma" +"Proxy-Authenticate" +"Proxy-Authorization" +"Proxy-Connection" +"Public-Key-Pins" +"Range" +"Referer" +"Refresh" +"Retry-After" +"Save-Data" +"Server" +"Set-Cookie" +"Status" +"Strict-Transport-Security" +"TE" +"Timing-Allow-Origin" +"Tk" +"Trailer" +"Transfer-Encoding" +"Upgrade" +"Upgrade-Insecure-Requests" +"User-Agent" +"Vary" +"Via" +"WWW-Authenticate" +"Warning" +"X-ATT-DeviceId" +"X-Content-Duration" +"X-Content-Security-Policy" +"X-Content-Type-Options" +"X-Correlation-ID" +"X-Csrf-Token" +"X-Forwarded-For" +"X-Forwarded-Host" +"X-Forwarded-Proto" +"X-Frame-Options" +"X-Http-Method-Override" +"X-Powered-By" +"X-Request-ID" +"X-Requested-With" +"X-UA-Compatible" +"X-UIDH" +"X-Wap-Profile" +"X-WebKit-CSP" +"X-XSS-Protection" + +# Source: string and character literals in httplib.h +" " +"&" +", " +"-" +"--" +"." +".." +":" +"=" +" = = " +"0123456789abcdef" +"%02X" +"%0A" +"\\x0a\\x0d" +"%0D" +"%20" +"%27" +"%2B" +"%2C" +"%3A" +"%3B" +"application/javascript" +"application/json" +"application/pdf" +"application/xhtml+xml" +"application/xml" +"application/x-www-form-urlencoded" +"Bad Request" +"boundary=" +"bytes=" +"chunked" +"close" +"CONNECT" +"css" +"Forbidden" +"Found" +"gif" +"gzip" +"html" +"ico" +"image/gif" +"image/jpg" +"image/png" +"image/svg+xml" +"image/x-icon" +"index.html" +"Internal Server Error" +"jpeg" +"js" +"json" +"Location" +"Moved Permanently" +"multipart/form-data" +"Not Found" +"Not Modified" +"OK" +"pdf" +"png" +"Range" +"REMOTE_ADDR" +"See Other" +"svg" +"text/" +"text/css" +"text/html" +"text/plain" +"txt" +"Unsupported Media Type" +"xhtml" +"xml" \ No newline at end of file diff --git a/test/fuzzing/standalone_fuzz_target_runner.cpp b/test/fuzzing/standalone_fuzz_target_runner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8bd5ed6ec9e0dc1878399714588956a460e17f5 --- /dev/null +++ b/test/fuzzing/standalone_fuzz_target_runner.cpp @@ -0,0 +1,35 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); + +// This runner does not do any fuzzing, but allows us to run the fuzz target +// on the test corpus or on a single file, +// e.g. the one that comes from a bug report. + +#include +#include +#include +#include + +// Forward declare the "fuzz target" interface. +// We deliberately keep this interface simple and header-free. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +// It reads all files passed as parameters and feeds their contents +// one by one into the fuzz target (LLVMFuzzerTestOneInput). +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::ifstream in(argv[i]); + in.seekg(0, in.end); + size_t length = static_cast(in.tellg()); + in.seekg(0, in.beg); + std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl; + // Allocate exactly length bytes so that we reliably catch buffer overflows. + std::vector bytes(length); + in.read(bytes.data(), static_cast(bytes.size())); + LLVMFuzzerTestOneInput(reinterpret_cast(bytes.data()), + bytes.size()); + std::cout << "Execution successful" << std::endl; + } + std::cout << "Execution finished" << std::endl; + return 0; +} diff --git a/test/gtest/gtest-all.cc b/test/gtest/gtest-all.cc new file mode 100644 index 0000000000000000000000000000000000000000..24e669e1e39b25e7d5880897c7096a9b647ccafb --- /dev/null +++ b/test/gtest/gtest-all.cc @@ -0,0 +1,12501 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// Google C++ Testing and Mocking Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +// GOOGLETEST_CM0004 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_ZOS +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT +# undef min + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# undef min + +#ifdef _MSC_VER +# include // NOLINT +#endif + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +#else + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +#endif + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utility functions and classes used by the Google C++ testing framework.// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_ +#define GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include +#include +#include + + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFailFast[] = "fail_fast"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kBriefFlag[] = "brief"; +const char kPrintTimeFlag[] = "print_time"; +const char kPrintUTF8Flag[] = "print_utf8"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; +const char kFlagfileFlag[] = "flagfile"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true if and only if the --help flag or an equivalent form +// is specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true if and only if Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, int32_t* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(int32_t random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + fail_fast_ = GTEST_FLAG(fail_fast); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + brief_ = GTEST_FLAG(brief); + print_time_ = GTEST_FLAG(print_time); + print_utf8_ = GTEST_FLAG(print_utf8); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(fail_fast) = fail_fast_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(brief) = brief_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(print_utf8) = print_utf8_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + bool fail_fast_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool brief_; + bool print_time_; + bool print_utf8_; + int32_t random_seed_; + int32_t repeat_; + bool shuffle_; + int32_t stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(uint32_t code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as a 32-bit integer. If it is unset, +// returns default_val. If it is not a 32-bit integer, prints an error and +// and aborts. +GTEST_API_ int32_t Int32FromEnvOrDie(const char* env_var, int32_t default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true if and only if the test should be run on this shard. The test id +// is some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value + : v[static_cast(i)]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = + begin + + static_cast(random->Generate(static_cast(range_width))); + std::swap((*v)[static_cast(selected)], + (*v)[static_cast(last_in_range)]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true if and only if the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true if and only if the user-specified filter matches the test + // suite name and the test name. + static bool FilterMatchesTest(const std::string& test_suite_name, + const std::string& test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() {} + + std::string CurrentStackTrace(int max_depth, int skip_count) override; + void UponLeavingGTest() override; + + private: +#if GTEST_HAS_ABSL + Mutex mutex_; // Protects all internal state. + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to the stack trace code from within the user code. + void* caller_frame_ = nullptr; +#endif // GTEST_HAS_ABSL + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const { return !Failed(); } + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const { + return failed_test_suite_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const { + const int index = GetElementOr(test_suite_indices_, i, -1); + return index < 0 ? nullptr : test_suites_[static_cast(i)]; + } + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const { return GetTestSuite(i); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableSuiteCase(int i) { + const int index = GetElementOr(test_suite_indices_, i, -1); + return index < 0 ? nullptr : test_suites_[static_cast(index)]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestSuite with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_suite_name: name of the test suite + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + TestCase* GetTestCase(const char* test_case_name, const char* type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc) { + return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc); + } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + // test_info: the TestInfo object + void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + TestInfo* test_info) { +#if GTEST_HAS_DEATH_TEST + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } +#endif // GTEST_HAS_DEATH_TEST + + GetTestSuite(test_info->test_suite_name(), test_info->type_param(), + set_up_tc, tear_down_tc) + ->AddTestInfo(test_info); + } + + // Returns ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } + + std::set* ignored_parameterized_test_suites() { + return &ignored_parameterized_test_suites_; + } + + // Returns TypeParameterizedTestSuiteRegistry object used to keep track of + // type-parameterized tests and instantiations of them. + internal::TypeParameterizedTestSuiteRegistry& + type_parameterized_test_registry() { + return type_parameterized_test_registry_; + } + + // Sets the TestSuite object for the test that's currently running. + void set_current_test_suite(TestSuite* a_current_test_suite) { + current_test_suite_ = a_current_test_suite; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_SUITE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_suites_, TestSuite::ClearTestSuiteResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test suite, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestSuite and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestSuite* current_test_suite() const { return current_test_suite_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test suites, and the tests within each test suite, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test suites and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestSuites in their original order. It owns the + // elements in the vector. + std::vector test_suites_; + + // Provides a level of indirection for the test suite list to allow + // easy shuffling and restoring the test suite order. The i-th + // element of this vector is the index of the i-th test suite in the + // shuffled order. + std::vector test_suite_indices_; + + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestSuiteRegistry parameterized_test_registry_; + internal::TypeParameterizedTestSuiteRegistry + type_parameterized_test_registry_; + + // The set holding the name of parameterized + // test suites that may go uninstantiated. + std::set ignored_parameterized_test_suites_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; + + // Index of the last death test suite registered. Initially -1. + int last_death_test_suite_; + + // This points to the TestSuite for the currently running test. It + // changes as Google Test goes through one test suite after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestSuite* current_test_suite_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True if and only if PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + std::unique_ptr internal_run_death_test_flag_; + std::unique_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + using BiggestConvertible = unsigned long long; // NOLINT + + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); // NOLINT + const bool parse_success = *end == '\0' && errno == 0; + + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const std::string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const std::string& message) { Send(message + "\n"); } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const std::string& host, const std::string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + ~SocketWriter() override { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + void Send(const std::string& message) override { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const auto len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != static_cast(len)) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() override { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const std::string host_name_; + const std::string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static std::string UrlEncode(const char* str); + + StreamingListener(const std::string& host, const std::string& port) + : socket_writer_(new SocketWriter(host, port)) { + Start(); + } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) override { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) override { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, + int iteration) override { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, + int /* iteration */) override { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + // Note that "event=TestCaseStart" is a wire format and has to remain + // "case" for compatibility + void OnTestCaseStart(const TestCase& test_case) override { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + // Note that "event=TestCaseEnd" is a wire format and has to remain + // "case" for compatibility + void OnTestCaseEnd(const TestCase& test_case) override { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) override { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) override { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) override { + const char* file_name = test_part_result.file_name(); + if (file_name == nullptr) file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const std::string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + std::string FormatBool(bool value) { return value ? "1" : "0"; } + + const std::unique_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_MAC +#ifndef GTEST_OS_IOS +#include +#endif +#endif + +#if GTEST_HAS_ABSL +#include "absl/debugging/failure_signal_handler.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test suite name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test suite whose name matches this filter is considered a death +// test suite and will be run before test suites whose name doesn't +// match this filter. +static const char kDeathTestSuiteFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output format. +static const char kDefaultOutputFormat[] = "xml"; +// The default output file. +static const char kDefaultOutputFile[] = "test_detail"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true if and only if the --help flag or an equivalent form +// is specified on the command line. +bool g_help_flag = false; + +// Utilty function to Open File for Writing +static FILE* OpenFileForWriting(const std::string& output_file) { + FILE* fileout = nullptr; + FilePath output_file_path(output_file); + FilePath output_dir(output_file_path.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + fileout = posix::FOpen(output_file.c_str(), "w"); + } + if (fileout == nullptr) { + GTEST_LOG_(FATAL) << "Unable to open file \"" << output_file << "\""; + } + return fileout; +} + +} // namespace internal + +// Bazel passes in the argument to '--test_filter' via the TESTBRIDGE_TEST_ONLY +// environment variable. +static const char* GetDefaultFilter() { + const char* const testbridge_test_only = + internal::posix::GetEnv("TESTBRIDGE_TEST_ONLY"); + if (testbridge_test_only != nullptr) { + return testbridge_test_only; + } + return kUniversalFilter; +} + +// Bazel passes in the argument to '--test_runner_fail_fast' via the +// TESTBRIDGE_TEST_RUNNER_FAIL_FAST environment variable. +static bool GetDefaultFailFast() { + const char* const testbridge_test_runner_fail_fast = + internal::posix::GetEnv("TESTBRIDGE_TEST_RUNNER_FAIL_FAST"); + if (testbridge_test_runner_fail_fast != nullptr) { + return strcmp(testbridge_test_runner_fail_fast, "1") == 0; + } + return false; +} + +GTEST_DEFINE_bool_( + fail_fast, internal::BoolFromGTestEnv("fail_fast", GetDefaultFailFast()), + "True if and only if a test failure should stop further test execution."); + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, internal::BoolFromGTestEnv("break_on_failure", false), + "True if and only if a failed assertion should be a debugger " + "break-point."); + +GTEST_DEFINE_bool_(catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True if and only if " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_( + install_failure_signal_handler, + internal::BoolFromGTestEnv("install_failure_signal_handler", false), + "If true and supported on the current platform, " GTEST_NAME_ " should " + "install a signal handler that dumps debugging information when fatal " + "signals are raised."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +// The net priority order after flag processing is thus: +// --gtest_output command line flag +// GTEST_OUTPUT environment variable +// XML_OUTPUT_FILE environment variable +// '' +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", + internal::OutputFlagAlsoCheckEnvVar().c_str()), + "A format (defaults to \"xml\" but can be specified to be \"json\"), " + "optionally followed by a colon and an output file name or directory. " + "A directory is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + brief, internal::BoolFromGTestEnv("brief", false), + "True if only test failures should be displayed in text output."); + +GTEST_DEFINE_bool_(print_time, internal::BoolFromGTestEnv("print_time", true), + "True if and only if " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_bool_(print_utf8, internal::BoolFromGTestEnv("print_utf8", true), + "True if and only if " GTEST_NAME_ + " prints UTF8 characters as text."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_(show_internal_stack_frames, false, + "True if and only if " GTEST_NAME_ + " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_(shuffle, internal::BoolFromGTestEnv("shuffle", false), + "True if and only if " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise. For use with an external test framework."); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DEFINE_string_( + flagfile, + internal::StringFromGTestEnv("flagfile", ""), + "This flag specifies the flagfile to read command-line flags from."); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +uint32_t Random::Generate(uint32_t range) { + // These constants are the same as are used in glibc's rand(3). + // Use wider types than necessary to prevent unsigned overflow diagnostics. + state_ = static_cast(1103515245ULL*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true if and only if the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +static bool GTestIsInitialized() { return GetArgvs().size() > 0; } + +// Iterates over a vector of TestSuites, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestSuiteList(const std::vector& case_list, + int (TestSuite::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true if and only if the test suite passed. +static bool TestSuitePassed(const TestSuite* test_suite) { + return test_suite->should_run() && test_suite->Passed(); +} + +// Returns true if and only if the test suite failed. +static bool TestSuiteFailed(const TestSuite* test_suite) { + return test_suite->should_run() && test_suite->Failed(); +} + +// Returns true if and only if test_suite contains at least one test that +// should run. +static bool ShouldRunTestSuite(const TestSuite* test_suite) { + return test_suite->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +namespace { + +// When TEST_P is found without a matching INSTANTIATE_TEST_SUITE_P +// to creates test cases for it, a synthetic test case is +// inserted to report ether an error or a log message. +// +// This configuration bit will likely be removed at some point. +constexpr bool kErrorOnUninstantiatedParameterizedTest = true; +constexpr bool kErrorOnUninstantiatedTypeParameterizedTest = true; + +// A test that fails at a given file/line location with a given message. +class FailureTest : public Test { + public: + explicit FailureTest(const CodeLocation& loc, std::string error_message, + bool as_error) + : loc_(loc), + error_message_(std::move(error_message)), + as_error_(as_error) {} + + void TestBody() override { + if (as_error_) { + AssertHelper(TestPartResult::kNonFatalFailure, loc_.file.c_str(), + loc_.line, "") = Message() << error_message_; + } else { + std::cout << error_message_ << std::endl; + } + } + + private: + const CodeLocation loc_; + const std::string error_message_; + const bool as_error_; +}; + + +} // namespace + +std::set* GetIgnoredParameterizedTestSuites() { + return UnitTest::GetInstance()->impl()->ignored_parameterized_test_suites(); +} + +// Add a given test_suit to the list of them allow to go un-instantiated. +MarkAsIgnored::MarkAsIgnored(const char* test_suite) { + GetIgnoredParameterizedTestSuites()->insert(test_suite); +} + +// If this parameterized test suite has no instantiations (and that +// has not been marked as okay), emit a test case reporting that. +void InsertSyntheticTestCase(const std::string& name, CodeLocation location, + bool has_test_p) { + const auto& ignored = *GetIgnoredParameterizedTestSuites(); + if (ignored.find(name) != ignored.end()) return; + + const char kMissingInstantiation[] = // + " is defined via TEST_P, but never instantiated. None of the test cases " + "will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only " + "ones provided expand to nothing." + "\n\n" + "Ideally, TEST_P definitions should only ever be included as part of " + "binaries that intend to use them. (As opposed to, for example, being " + "placed in a library that may be linked in to get other utilities.)"; + + const char kMissingTestCase[] = // + " is instantiated via INSTANTIATE_TEST_SUITE_P, but no tests are " + "defined via TEST_P . No test cases will run." + "\n\n" + "Ideally, INSTANTIATE_TEST_SUITE_P should only ever be invoked from " + "code that always depend on code that provides TEST_P. Failing to do " + "so is often an indication of dead code, e.g. the last TEST_P was " + "removed but the rest got left behind."; + + std::string message = + "Parameterized test suite " + name + + (has_test_p ? kMissingInstantiation : kMissingTestCase) + + "\n\n" + "To suppress this error for this test suite, insert the following line " + "(in a non-header) in the namespace it is defined in:" + "\n\n" + "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" + name + ");"; + + std::string full_name = "UninstantiatedParameterizedTestSuite<" + name + ">"; + RegisterTest( // + "GoogleTestVerification", full_name.c_str(), + nullptr, // No type parameter. + nullptr, // No value parameter. + location.file.c_str(), location.line, [message, location] { + return new FailureTest(location, message, + kErrorOnUninstantiatedParameterizedTest); + }); +} + +void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location) { + GetUnitTestImpl()->type_parameterized_test_registry().RegisterTestSuite( + test_suite_name, code_location); +} + +void RegisterTypeParameterizedTestSuiteInstantiation(const char* case_name) { + GetUnitTestImpl() + ->type_parameterized_test_registry() + .RegisterInstantiation(case_name); +} + +void TypeParameterizedTestSuiteRegistry::RegisterTestSuite( + const char* test_suite_name, CodeLocation code_location) { + suites_.emplace(std::string(test_suite_name), + TypeParameterizedTestSuiteInfo(code_location)); +} + +void TypeParameterizedTestSuiteRegistry::RegisterInstantiation( + const char* test_suite_name) { + auto it = suites_.find(std::string(test_suite_name)); + if (it != suites_.end()) { + it->second.instantiated = true; + } else { + GTEST_LOG_(ERROR) << "Unknown type parameterized test suit '" + << test_suite_name << "'"; + } +} + +void TypeParameterizedTestSuiteRegistry::CheckForInstantiations() { + const auto& ignored = *GetIgnoredParameterizedTestSuites(); + for (const auto& testcase : suites_) { + if (testcase.second.instantiated) continue; + if (ignored.find(testcase.first) != ignored.end()) continue; + + std::string message = + "Type parameterized test suite " + testcase.first + + " is defined via REGISTER_TYPED_TEST_SUITE_P, but never instantiated " + "via INSTANTIATE_TYPED_TEST_SUITE_P. None of the test cases will run." + "\n\n" + "Ideally, TYPED_TEST_P definitions should only ever be included as " + "part of binaries that intend to use them. (As opposed to, for " + "example, being placed in a library that may be linked in to get other " + "utilities.)" + "\n\n" + "To suppress this error for this test suite, insert the following line " + "(in a non-header) in the namespace it is defined in:" + "\n\n" + "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" + + testcase.first + ");"; + + std::string full_name = + "UninstantiatedTypeParameterizedTestSuite<" + testcase.first + ">"; + RegisterTest( // + "GoogleTestVerification", full_name.c_str(), + nullptr, // No type parameter. + nullptr, // No value parameter. + testcase.second.code_location.file.c_str(), + testcase.second.code_location.line, [message, testcase] { + return new FailureTest(testcase.second.code_location, message, + kErrorOnUninstantiatedTypeParameterizedTest); + }); + } +} + +// A copy of all command line arguments. Set by InitGoogleTest(). +static ::std::vector g_argvs; + +::std::vector GetArgvs() { +#if defined(GTEST_CUSTOM_GET_ARGVS_) + // GTEST_CUSTOM_GET_ARGVS_() may return a container of std::string or + // ::string. This code converts it to the appropriate type. + const auto& custom = GTEST_CUSTOM_GET_ARGVS_(); + return ::std::vector(custom.begin(), custom.end()); +#else // defined(GTEST_CUSTOM_GET_ARGVS_) + return g_argvs; +#endif // defined(GTEST_CUSTOM_GET_ARGVS_) +} + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS || GTEST_OS_OS2 + result.Set(FilePath(GetArgvs()[0]).RemoveExtension("exe")); +#else + result.Set(FilePath(GetArgvs()[0])); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == nullptr) + ? std::string(gtest_output_flag) + : std::string(gtest_output_flag, + static_cast(colon - gtest_output_flag)); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + + std::string format = GetOutputFormat(); + if (format.empty()) + format = std::string(kDefaultOutputFormat); + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == nullptr) + return internal::FilePath::MakeFileName( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile), 0, + format.c_str()).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true if and only if the wildcard pattern matches the string. Each +// pattern consists of regular characters, single-character wildcards (?), and +// multi-character wildcards (*). +// +// This function implements a linear-time string globbing algorithm based on +// https://research.swtch.com/glob. +static bool PatternMatchesString(const std::string& name_str, + const char* pattern, const char* pattern_end) { + const char* name = name_str.c_str(); + const char* const name_begin = name; + const char* const name_end = name + name_str.size(); + + const char* pattern_next = pattern; + const char* name_next = name; + + while (pattern < pattern_end || name < name_end) { + if (pattern < pattern_end) { + switch (*pattern) { + default: // Match an ordinary character. + if (name < name_end && *name == *pattern) { + ++pattern; + ++name; + continue; + } + break; + case '?': // Match any single character. + if (name < name_end) { + ++pattern; + ++name; + continue; + } + break; + case '*': + // Match zero or more characters. Start by skipping over the wildcard + // and matching zero characters from name. If that fails, restart and + // match one more character than the last attempt. + pattern_next = pattern; + name_next = name + 1; + ++pattern; + continue; + } + } + // Failed to match a character. Restart if possible. + if (name_begin < name_next && name_next <= name_end) { + pattern = pattern_next; + name = name_next; + continue; + } + return false; + } + return true; +} + +bool UnitTestOptions::MatchesFilter(const std::string& name_str, + const char* filter) { + // The filter is a list of patterns separated by colons (:). + const char* pattern = filter; + while (true) { + // Find the bounds of this pattern. + const char* const next_sep = strchr(pattern, ':'); + const char* const pattern_end = + next_sep != nullptr ? next_sep : pattern + strlen(pattern); + + // Check if this pattern matches name_str. + if (PatternMatchesString(name_str, pattern, pattern_end)) { + return true; + } + + // Give up on this pattern. However, if we found a pattern separator (:), + // advance to the next pattern (skipping over the separator) and restart. + if (next_sep == nullptr) { + return false; + } + pattern = next_sep + 1; + } + return true; +} + +// Returns true if and only if the user-specified filter matches the test +// suite name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string& test_suite_name, + const std::string& test_name) { + const std::string& full_name = test_suite_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == nullptr) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +static AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const std::string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == nullptr) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker::SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const std::string& substr) + : results_(results), type_(type), substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test suites. +int UnitTestImpl::successful_test_suite_count() const { + return CountIf(test_suites_, TestSuitePassed); +} + +// Gets the number of failed test suites. +int UnitTestImpl::failed_test_suite_count() const { + return CountIf(test_suites_, TestSuiteFailed); +} + +// Gets the number of all test suites. +int UnitTestImpl::total_test_suite_count() const { + return static_cast(test_suites_.size()); +} + +// Gets the number of all test suites that contain at least one test +// that should run. +int UnitTestImpl::test_suite_to_run_count() const { + return CountIf(test_suites_, ShouldRunTestSuite); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::successful_test_count); +} + +// Gets the number of skipped tests. +int UnitTestImpl::skipped_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::skipped_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestSuiteList(test_suites_, + &TestSuite::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + return os_stack_trace_getter()->CurrentStackTrace( + static_cast(GTEST_FLAG(stack_trace_depth)), + skip_count + 1 + // Skips the user-specified number of frames plus this function + // itself. + ); // NOLINT +} + +// A helper class for measuring elapsed times. +class Timer { + public: + Timer() : start_(std::chrono::steady_clock::now()) {} + + // Return time elapsed in milliseconds since the timer was created. + TimeInMillis Elapsed() { + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - start_) + .count(); + } + + private: + std::chrono::steady_clock::time_point start_; +}; + +// Returns a timestamp as milliseconds since the epoch. Note this time may jump +// around subject to adjustments by the system, to measure elapsed time use +// Timer instead. +TimeInMillis GetTimeInMillis() { + return std::chrono::duration_cast( + std::chrono::system_clock::now() - + std::chrono::system_clock::from_time_t(0)) + .count(); +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return nullptr; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, nullptr, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return nullptr; + const int ansi_length = WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, nullptr, + 0, nullptr, nullptr); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, ansi, ansi_length, nullptr, + nullptr); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true if and only if they have the same +// content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if (lhs == nullptr) return rhs == nullptr; + + if (rhs == nullptr) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING + +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != nullptr + ? new ::std::string(*other.message_) + : static_cast< ::std::string*>(nullptr)) {} + +// Swaps two AssertionResults. +void AssertionResult::swap(AssertionResult& other) { + using std::swap; + swap(success_, other.success_); + swap(message_, other.message_); +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != nullptr) negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +namespace edit_distance { +std::vector CalculateOptimalEdits(const std::vector& left, + const std::vector& right) { + std::vector > costs( + left.size() + 1, std::vector(right.size() + 1)); + std::vector > best_move( + left.size() + 1, std::vector(right.size() + 1)); + + // Populate for empty right. + for (size_t l_i = 0; l_i < costs.size(); ++l_i) { + costs[l_i][0] = static_cast(l_i); + best_move[l_i][0] = kRemove; + } + // Populate for empty left. + for (size_t r_i = 1; r_i < costs[0].size(); ++r_i) { + costs[0][r_i] = static_cast(r_i); + best_move[0][r_i] = kAdd; + } + + for (size_t l_i = 0; l_i < left.size(); ++l_i) { + for (size_t r_i = 0; r_i < right.size(); ++r_i) { + if (left[l_i] == right[r_i]) { + // Found a match. Consume it. + costs[l_i + 1][r_i + 1] = costs[l_i][r_i]; + best_move[l_i + 1][r_i + 1] = kMatch; + continue; + } + + const double add = costs[l_i + 1][r_i]; + const double remove = costs[l_i][r_i + 1]; + const double replace = costs[l_i][r_i]; + if (add < remove && add < replace) { + costs[l_i + 1][r_i + 1] = add + 1; + best_move[l_i + 1][r_i + 1] = kAdd; + } else if (remove < add && remove < replace) { + costs[l_i + 1][r_i + 1] = remove + 1; + best_move[l_i + 1][r_i + 1] = kRemove; + } else { + // We make replace a little more expensive than add/remove to lower + // their priority. + costs[l_i + 1][r_i + 1] = replace + 1.00001; + best_move[l_i + 1][r_i + 1] = kReplace; + } + } + } + + // Reconstruct the best path. We do it in reverse order. + std::vector best_path; + for (size_t l_i = left.size(), r_i = right.size(); l_i > 0 || r_i > 0;) { + EditType move = best_move[l_i][r_i]; + best_path.push_back(move); + l_i -= move != kAdd; + r_i -= move != kRemove; + } + std::reverse(best_path.begin(), best_path.end()); + return best_path; +} + +namespace { + +// Helper class to convert string into ids with deduplication. +class InternalStrings { + public: + size_t GetId(const std::string& str) { + IdMap::iterator it = ids_.find(str); + if (it != ids_.end()) return it->second; + size_t id = ids_.size(); + return ids_[str] = id; + } + + private: + typedef std::map IdMap; + IdMap ids_; +}; + +} // namespace + +std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right) { + std::vector left_ids, right_ids; + { + InternalStrings intern_table; + for (size_t i = 0; i < left.size(); ++i) { + left_ids.push_back(intern_table.GetId(left[i])); + } + for (size_t i = 0; i < right.size(); ++i) { + right_ids.push_back(intern_table.GetId(right[i])); + } + } + return CalculateOptimalEdits(left_ids, right_ids); +} + +namespace { + +// Helper class that holds the state for one hunk and prints it out to the +// stream. +// It reorders adds/removes when possible to group all removes before all +// adds. It also adds the hunk header before printint into the stream. +class Hunk { + public: + Hunk(size_t left_start, size_t right_start) + : left_start_(left_start), + right_start_(right_start), + adds_(), + removes_(), + common_() {} + + void PushLine(char edit, const char* line) { + switch (edit) { + case ' ': + ++common_; + FlushEdits(); + hunk_.push_back(std::make_pair(' ', line)); + break; + case '-': + ++removes_; + hunk_removes_.push_back(std::make_pair('-', line)); + break; + case '+': + ++adds_; + hunk_adds_.push_back(std::make_pair('+', line)); + break; + } + } + + void PrintTo(std::ostream* os) { + PrintHeader(os); + FlushEdits(); + for (std::list >::const_iterator it = + hunk_.begin(); + it != hunk_.end(); ++it) { + *os << it->first << it->second << "\n"; + } + } + + bool has_edits() const { return adds_ || removes_; } + + private: + void FlushEdits() { + hunk_.splice(hunk_.end(), hunk_removes_); + hunk_.splice(hunk_.end(), hunk_adds_); + } + + // Print a unified diff header for one hunk. + // The format is + // "@@ -, +, @@" + // where the left/right parts are omitted if unnecessary. + void PrintHeader(std::ostream* ss) const { + *ss << "@@ "; + if (removes_) { + *ss << "-" << left_start_ << "," << (removes_ + common_); + } + if (removes_ && adds_) { + *ss << " "; + } + if (adds_) { + *ss << "+" << right_start_ << "," << (adds_ + common_); + } + *ss << " @@\n"; + } + + size_t left_start_, right_start_; + size_t adds_, removes_, common_; + std::list > hunk_, hunk_adds_, hunk_removes_; +}; + +} // namespace + +// Create a list of diff hunks in Unified diff format. +// Each hunk has a header generated by PrintHeader above plus a body with +// lines prefixed with ' ' for no change, '-' for deletion and '+' for +// addition. +// 'context' represents the desired unchanged prefix/suffix around the diff. +// If two hunks are close enough that their contexts overlap, then they are +// joined into one hunk. +std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context) { + const std::vector edits = CalculateOptimalEdits(left, right); + + size_t l_i = 0, r_i = 0, edit_i = 0; + std::stringstream ss; + while (edit_i < edits.size()) { + // Find first edit. + while (edit_i < edits.size() && edits[edit_i] == kMatch) { + ++l_i; + ++r_i; + ++edit_i; + } + + // Find the first line to include in the hunk. + const size_t prefix_context = std::min(l_i, context); + Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1); + for (size_t i = prefix_context; i > 0; --i) { + hunk.PushLine(' ', left[l_i - i].c_str()); + } + + // Iterate the edits until we found enough suffix for the hunk or the input + // is over. + size_t n_suffix = 0; + for (; edit_i < edits.size(); ++edit_i) { + if (n_suffix >= context) { + // Continue only if the next hunk is very close. + auto it = edits.begin() + static_cast(edit_i); + while (it != edits.end() && *it == kMatch) ++it; + if (it == edits.end() || + static_cast(it - edits.begin()) - edit_i >= context) { + // There is no next edit or it is too far away. + break; + } + } + + EditType edit = edits[edit_i]; + // Reset count when a non match is found. + n_suffix = edit == kMatch ? n_suffix + 1 : 0; + + if (edit == kMatch || edit == kRemove || edit == kReplace) { + hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str()); + } + if (edit == kAdd || edit == kReplace) { + hunk.PushLine('+', right[r_i].c_str()); + } + + // Advance indices, depending on edit type. + l_i += edit != kAdd; + r_i += edit != kRemove; + } + + if (!hunk.has_edits()) { + // We are done. We don't want this hunk. + break; + } + + hunk.PrintTo(&ss); + } + return ss.str(); +} + +} // namespace edit_distance + +namespace { + +// The string representation of the values received in EqFailure() are already +// escaped. Split them on escaped '\n' boundaries. Leave all other escaped +// characters the same. +std::vector SplitEscapedString(const std::string& str) { + std::vector lines; + size_t start = 0, end = str.size(); + if (end > 2 && str[0] == '"' && str[end - 1] == '"') { + ++start; + --end; + } + bool escaped = false; + for (size_t i = start; i + 1 < end; ++i) { + if (escaped) { + escaped = false; + if (str[i] == 'n') { + lines.push_back(str.substr(start, i - start - 1)); + start = i + 1; + } + } else { + escaped = str[i] == '\\'; + } + } + lines.push_back(str.substr(start, end - start)); + return lines; +} + +} // namespace + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// lhs_expression: "foo" +// rhs_expression: "bar" +// lhs_value: "5" +// rhs_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string "Ignoring case" will +// be inserted into the message. +AssertionResult EqFailure(const char* lhs_expression, + const char* rhs_expression, + const std::string& lhs_value, + const std::string& rhs_value, + bool ignoring_case) { + Message msg; + msg << "Expected equality of these values:"; + msg << "\n " << lhs_expression; + if (lhs_value != lhs_expression) { + msg << "\n Which is: " << lhs_value; + } + msg << "\n " << rhs_expression; + if (rhs_value != rhs_expression) { + msg << "\n Which is: " << rhs_value; + } + + if (ignoring_case) { + msg << "\nIgnoring case"; + } + + if (!lhs_value.empty() && !rhs_value.empty()) { + const std::vector lhs_lines = + SplitEscapedString(lhs_value); + const std::vector rhs_lines = + SplitEscapedString(rhs_value); + if (lhs_lines.size() > 1 || rhs_lines.size() > 1) { + msg << "\nWith diff:\n" + << edit_distance::CreateUnifiedDiff(lhs_lines, rhs_lines); + } + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // Find the value which is closest to zero. + const double min_abs = std::min(fabs(val1), fabs(val2)); + // Find the distance to the next double from that value. + const double epsilon = + nextafter(min_abs, std::numeric_limits::infinity()) - min_abs; + // Detect the case where abs_error is so small that EXPECT_NEAR is + // effectively the same as EXPECT_EQUAL, and give an informative error + // message so that the situation can be more easily understood without + // requiring exotic floating-point knowledge. + // Don't do an epsilon check if abs_error is zero because that implies + // that an equality check was actually intended. + if (!(std::isnan)(val1) && !(std::isnan)(val2) && abs_error > 0 && + abs_error < epsilon) { + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 << " is " + << diff << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ".\nThe abs_error parameter " + << abs_error_expr << " evaluates to " << abs_error + << " which is smaller than the minimum distance between doubles for " + "numbers of this magnitude which is " + << epsilon + << ", thus making this EXPECT_NEAR check equivalent to " + "EXPECT_EQUAL. Consider using EXPECT_DOUBLE_EQ instead."; + } + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* lhs_expression, + const char* rhs_expression, + const char* lhs, + const char* rhs) { + if (String::CStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* lhs_expression, + const char* rhs_expression, + const char* lhs, + const char* rhs) { + if (String::CaseInsensitiveCStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true if and only if needle +// is a substring of haystack. NULL is considered a substring of +// itself only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == nullptr || haystack == nullptr) return needle == haystack; + + return strstr(haystack, needle) != nullptr; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == nullptr || haystack == nullptr) return needle == haystack; + + return wcsstr(haystack, needle) != nullptr; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + static_cast(hr), // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + nullptr); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have up to 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline uint32_t ChopLowBits(uint32_t* bits, int n) { + const uint32_t low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type uint32_t because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(uint32_t code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexUInt32(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline uint32_t CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const auto first_u = static_cast(first); + const auto second_u = static_cast(second); + const uint32_t mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) + ? (((first_u & mask) << 10) | (second_u & mask)) + 0x10000 + : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + first_u; +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + uint32_t unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == nullptr) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true if and only if they have the +// same content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == nullptr) return rhs == nullptr; + + if (rhs == nullptr) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* lhs_expression, + const char* rhs_expression, + const wchar_t* lhs, + const wchar_t* rhs) { + if (String::WideCStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true if and only if they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == nullptr) return rhs == nullptr; + if (rhs == nullptr) return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + +// Compares two wide C strings, ignoring case. Returns true if and only if they +// have the same content. +// +// Unlike wcscasecmp(), this function can handle NULL argument(s). +// A NULL C string is considered different to any non-NULL wide C string, +// including the empty string. +// NB: The implementations on different platforms slightly differ. +// On windows, this method uses _wcsicmp which compares according to LC_CTYPE +// environment variable. On GNU platform this method uses wcscasecmp +// which compares according to LC_CTYPE category of the current locale. +// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the +// current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == nullptr) return rhs == nullptr; + + if (rhs == nullptr) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(static_cast(*lhs++)); + right = towlower(static_cast(*rhs++)); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true if and only if str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + return FormatIntWidthN(value, 2); +} + +// Formats an int value to given width with leading zeros. +std::string String::FormatIntWidthN(int value, int width) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(width) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexUInt32(uint32_t value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + return FormatHexUInt32(static_cast(value)); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(static_cast(2 * (end - start))); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + if (gtest_msg.empty()) { + return user_msg_string; + } + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), start_timestamp_(0), elapsed_time_(0) {} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(static_cast(i)); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(static_cast(i)); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properties_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", "errors", "failures", "name", + "tests", "time", "timestamp", "skipped"}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", "name", "status", "time", "type_param", + "value_param", "file", "line"}; + +// Use a slightly different set for allowed output to ensure existing tests can +// still RecordProperty("result") or "RecordProperty(timestamp") +static const char* const kReservedOutputTestCaseAttributes[] = { + "classname", "name", "status", "time", "type_param", + "value_param", "file", "line", "result", "timestamp"}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +// TODO(jdesprez): Merge the two getReserved attributes once skip is improved +static std::vector GetReservedOutputAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedOutputTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +static bool ValidateTestPropertyName( + const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true off the test part was skipped. +static bool TestPartSkipped(const TestPartResult& result) { + return result.skipped(); +} + +// Returns true if and only if the test was skipped. +bool TestResult::Skipped() const { + return !Failed() && CountIf(test_part_results_, TestPartSkipped) > 0; +} + +// Returns true if and only if the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true if and only if the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true if and only if the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true if and only if the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true if and only if the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the states of all flags. +Test::Test() + : gtest_flag_saver_(new GTEST_FLAG_SAVER_) { +} + +// The d'tor restores the states of all flags. The actual work is +// done by the d'tor of the gtest_flag_saver_ field, and thus not +// visible here. +Test::~Test() { +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + nullptr, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test suite to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test suite. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestSuite* const test_suite = impl->current_test_suite(); + + // Info about the first test in the current test suite. + const TestInfo* const first_test_info = test_suite->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // Both TEST and TEST_F appear in same test suite, which is incorrect. + // Tell the user how to fix this. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test suite must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test suite is\n" + << "illegal. In test suite " << this_test_info->test_suite_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // Two fixture classes with the same name appear in two different + // namespaces, which is not allowed. Tell the user how to fix this. + ADD_FAILURE() + << "All tests in the same test suite must use the same test fixture\n" + << "class. However, in test suite " + << this_test_info->test_suite_name() << ",\n" + << "you defined test " << first_test_name << " and test " + << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test suites."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != nullptr) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const AssertionException&) { // NOLINT + // This failure was reported already. + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(nullptr, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful and didn't call + // GTEST_SKIP(). + if (!HasFatalFailure() && !IsSkipped()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true if and only if the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true if and only if the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// Returns true if and only if the current test was skipped. +bool Test::IsSkipped() { + return internal::GetUnitTestImpl()->current_test_result()->Skipped(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_suite_name, + const std::string& a_name, const char* a_type_param, + const char* a_value_param, + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_suite_name_(a_test_suite_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : nullptr), + value_param_(a_value_param ? new std::string(a_value_param) : nullptr), + location_(a_code_location), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + is_in_another_shard_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_suite_name, name, type_param, value_param, + code_location, fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location) { + Message errors; + errors + << "Attempted redefinition of test suite " << test_suite_name << ".\n" + << "All tests in the same test suite must use the same test fixture\n" + << "class. However, in test suite " << test_suite_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test suites."; + + GTEST_LOG_(ERROR) << FormatFileLocation(code_location.file.c_str(), + code_location.line) + << " " << errors.GetString(); +} +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestSuite class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true if and only if the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_SUITE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + type_parameterized_test_registry_.CheckForInstantiations(); + parameterized_tests_registered_ = true; + } +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + result_.set_start_timestamp(internal::GetTimeInMillis()); + internal::Timer timer; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test if the constructor didn't generate a fatal failure or invoke + // GTEST_SKIP(). + // Note that the object will not be null + if (!Test::HasFatalFailure() && !Test::IsSkipped()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + if (test != nullptr) { + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + } + + result_.set_elapsed_time(timer.Elapsed()); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(nullptr); +} + +// Skip and records a skipped test result for this object. +void TestInfo::Skip() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TestPartResult test_part_result = + TestPartResult(TestPartResult::kSkip, this->file(), this->line(), ""); + impl->GetTestPartResultReporterForCurrentThread()->ReportTestPartResult( + test_part_result); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + impl->set_current_test_info(nullptr); +} + +// class TestSuite + +// Gets the number of successful tests in this test suite. +int TestSuite::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of successful tests in this test suite. +int TestSuite::skipped_test_count() const { + return CountIf(test_info_list_, TestSkipped); +} + +// Gets the number of failed tests in this test suite. +int TestSuite::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestSuite::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test suite. +int TestSuite::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestSuite::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test suite that should run. +int TestSuite::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestSuite::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestSuite with the given name. +// +// Arguments: +// +// a_name: name of the test suite +// a_type_param: the name of the test suite's type parameter, or NULL if +// this is not a typed or a type-parameterized test suite. +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +TestSuite::TestSuite(const char* a_name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : nullptr), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + start_timestamp_(0), + elapsed_time_(0) {} + +// Destructor of TestSuite. +TestSuite::~TestSuite() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestSuite::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? nullptr : test_info_list_[static_cast(index)]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestSuite::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? nullptr : test_info_list_[static_cast(index)]; +} + +// Adds a test to this test suite. Will delete the test upon +// destruction of the TestSuite object. +void TestSuite::AddTestInfo(TestInfo* test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestSuite. +void TestSuite::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_suite(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Call both legacy and the new API + repeater->OnTestSuiteStart(*this); +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseStart(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()"); + + start_timestamp_ = internal::GetTimeInMillis(); + internal::Timer timer; + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + if (GTEST_FLAG(fail_fast) && GetMutableTestInfo(i)->result()->Failed()) { + for (int j = i + 1; j < total_test_count(); j++) { + GetMutableTestInfo(j)->Skip(); + } + break; + } + } + elapsed_time_ = timer.Elapsed(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestSuite::RunTearDownTestSuite, "TearDownTestSuite()"); + + // Call both legacy and the new API + repeater->OnTestSuiteEnd(*this); +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseEnd(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + impl->set_current_test_suite(nullptr); +} + +// Skips all tests under this TestSuite. +void TestSuite::Skip() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_suite(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Call both legacy and the new API + repeater->OnTestSuiteStart(*this); +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseStart(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Skip(); + } + + // Call both legacy and the new API + repeater->OnTestSuiteEnd(*this); + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseEnd(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + impl->set_current_test_suite(nullptr); +} + +// Clears the results of all tests in this test suite. +void TestSuite::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test suite. +void TestSuite::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestSuite::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test suites. +static std::string FormatTestSuiteCount(int test_suite_count) { + return FormatCountableNoun(test_suite_count, "test suite", "test suites"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSkip: + return "Skipped\n"; + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { +namespace { +enum class GTestColor { kDefault, kRed, kGreen, kYellow }; +} // namespace + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW + +// Returns the character attribute for the given color. +static WORD GetColorAttribute(GTestColor color) { + switch (color) { + case GTestColor::kRed: + return FOREGROUND_RED; + case GTestColor::kGreen: + return FOREGROUND_GREEN; + case GTestColor::kYellow: + return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +static int GetBitOffset(WORD color_mask) { + if (color_mask == 0) return 0; + + int bitOffset = 0; + while ((color_mask & 1) == 0) { + color_mask >>= 1; + ++bitOffset; + } + return bitOffset; +} + +static WORD GetNewColor(GTestColor color, WORD old_color_attrs) { + // Let's reuse the BG + static const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN | + BACKGROUND_RED | BACKGROUND_INTENSITY; + static const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN | + FOREGROUND_RED | FOREGROUND_INTENSITY; + const WORD existing_bg = old_color_attrs & background_mask; + + WORD new_color = + GetColorAttribute(color) | existing_bg | FOREGROUND_INTENSITY; + static const int bg_bitOffset = GetBitOffset(background_mask); + static const int fg_bitOffset = GetBitOffset(foreground_mask); + + if (((new_color & background_mask) >> bg_bitOffset) == + ((new_color & foreground_mask) >> fg_bitOffset)) { + new_color ^= FOREGROUND_INTENSITY; // invert intensity + } + return new_color; +} + +#else + +// Returns the ANSI color code for the given color. GTestColor::kDefault is +// an invalid input. +static const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case GTestColor::kRed: + return "1"; + case GTestColor::kGreen: + return "2"; + case GTestColor::kYellow: + return "3"; + default: + return nullptr; + } +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true if and only if Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "tmux") || + String::CStringEquals(term, "tmux-256color") || + String::CStringEquals(term, "rxvt-unicode") || + String::CStringEquals(term, "rxvt-unicode-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. + +GTEST_ATTRIBUTE_PRINTF_(2, 3) +static void ColoredPrintf(GTestColor color, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS || GTEST_OS_IOS || \ + GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT || defined(ESP_PLATFORM) + const bool use_color = AlwaysFalse(); +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != GTestColor::kDefault); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + const WORD new_color = GetNewColor(color, old_color_attrs); + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, new_color); + + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gtest_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +static void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != nullptr || value_param != nullptr) { + printf(", where "); + if (type_param != nullptr) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != nullptr) printf(" and "); + } + if (value_param != nullptr) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char* test_suite, const char* test) { + printf("%s.%s", test_suite, test); + } + + // The following methods override what's in the TestEventListener class. + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& unit_test, int iteration) override; + void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override; + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& test_case) override; +#else + void OnTestSuiteStart(const TestSuite& test_suite) override; +#endif // OnTestCaseStart + + void OnTestStart(const TestInfo& test_info) override; + + void OnTestPartResult(const TestPartResult& result) override; + void OnTestEnd(const TestInfo& test_info) override; +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& test_case) override; +#else + void OnTestSuiteEnd(const TestSuite& test_suite) override; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override; + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); + static void PrintFailedTestSuites(const UnitTest& unit_test); + static void PrintSkippedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(GTestColor::kYellow, "Note: %s filter = %s\n", GTEST_NAME_, + filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const int32_t shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(GTestColor::kYellow, "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(GTestColor::kYellow, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(GTestColor::kGreen, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == nullptr) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} +#else +void PrettyUnitTestResultPrinter::OnTestSuiteStart( + const TestSuite& test_suite) { + const std::string counts = + FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s", counts.c_str(), test_suite.name()); + if (test_suite.type_param() == nullptr) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_suite.type_param()); + } + fflush(stdout); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(GTestColor::kGreen, "[ RUN ] "); + PrintTestName(test_info.test_suite_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + switch (result.type()) { + // If the test part succeeded, we don't need to do anything. + case TestPartResult::kSuccess: + return; + default: + // Print failure message from the assertion + // (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); + } +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(GTestColor::kGreen, "[ OK ] "); + } else if (test_info.result()->Skipped()) { + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + } else { + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + } + PrintTestName(test_info.test_suite_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} +#else +void PrettyUnitTestResultPrinter::OnTestSuiteEnd(const TestSuite& test_suite) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_suite.name(), + internal::StreamableToString(test_suite.elapsed_time()).c_str()); + fflush(stdout); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + const TestSuite& test_suite = *unit_test.GetTestSuite(i); + if (!test_suite.should_run() || (test_suite.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_suite.total_test_count(); ++j) { + const TestInfo& test_info = *test_suite.GetTestInfo(j); + if (!test_info.should_run() || !test_info.result()->Failed()) { + continue; + } + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + printf("%s.%s", test_suite.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } + printf("\n%2d FAILED %s\n", failed_test_count, + failed_test_count == 1 ? "TEST" : "TESTS"); +} + +// Internal helper for printing the list of test suite failures not covered by +// PrintFailedTests. +void PrettyUnitTestResultPrinter::PrintFailedTestSuites( + const UnitTest& unit_test) { + int suite_failure_count = 0; + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + const TestSuite& test_suite = *unit_test.GetTestSuite(i); + if (!test_suite.should_run()) { + continue; + } + if (test_suite.ad_hoc_test_result().Failed()) { + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + printf("%s: SetUpTestSuite or TearDownTestSuite\n", test_suite.name()); + ++suite_failure_count; + } + } + if (suite_failure_count > 0) { + printf("\n%2d FAILED TEST %s\n", suite_failure_count, + suite_failure_count == 1 ? "SUITE" : "SUITES"); + } +} + +// Internal helper for printing the list of skipped tests. +void PrettyUnitTestResultPrinter::PrintSkippedTests(const UnitTest& unit_test) { + const int skipped_test_count = unit_test.skipped_test_count(); + if (skipped_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + const TestSuite& test_suite = *unit_test.GetTestSuite(i); + if (!test_suite.should_run() || (test_suite.skipped_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_suite.total_test_count(); ++j) { + const TestInfo& test_info = *test_suite.GetTestInfo(j); + if (!test_info.should_run() || !test_info.result()->Skipped()) { + continue; + } + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + printf("%s.%s", test_suite.name(), test_info.name()); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(GTestColor::kGreen, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(GTestColor::kGreen, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + const int skipped_test_count = unit_test.skipped_test_count(); + if (skipped_test_count > 0) { + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + printf("%s, listed below:\n", FormatTestCount(skipped_test_count).c_str()); + PrintSkippedTests(unit_test); + } + + if (!unit_test.Passed()) { + PrintFailedTests(unit_test); + PrintFailedTestSuites(unit_test); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (unit_test.Passed()) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(GTestColor::kYellow, " YOU HAVE %d DISABLED %s\n\n", + num_disabled, num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// This class implements the TestEventListener interface. +// +// Class BriefUnitTestResultPrinter is copyable. +class BriefUnitTestResultPrinter : public TestEventListener { + public: + BriefUnitTestResultPrinter() {} + static void PrintTestName(const char* test_suite, const char* test) { + printf("%s.%s", test_suite, test); + } + + // The following methods override what's in the TestEventListener class. + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#else + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +#endif // OnTestCaseStart + + void OnTestStart(const TestInfo& /*test_info*/) override {} + + void OnTestPartResult(const TestPartResult& result) override; + void OnTestEnd(const TestInfo& test_info) override; +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#else + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// Called after an assertion failure. +void BriefUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + switch (result.type()) { + // If the test part succeeded, we don't need to do anything. + case TestPartResult::kSuccess: + return; + default: + // Print failure message from the assertion + // (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); + } +} + +void BriefUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Failed()) { + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + PrintTestName(test_info.test_suite_name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", + internal::StreamableToString(test_info.result()->elapsed_time()) + .c_str()); + } else { + printf("\n"); + } + fflush(stdout); + } +} + +void BriefUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(GTestColor::kGreen, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(GTestColor::kGreen, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + const int skipped_test_count = unit_test.skipped_test_count(); + if (skipped_test_count > 0) { + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + printf("%s.\n", FormatTestCount(skipped_test_count).c_str()); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (unit_test.Passed()) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(GTestColor::kYellow, " YOU HAVE %d DISABLED %s\n\n", + num_disabled, num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End BriefUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + ~TestEventRepeater() override; + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + void OnTestProgramStart(const UnitTest& unit_test) override; + void OnTestIterationStart(const UnitTest& unit_test, int iteration) override; + void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override; + void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) override; +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestSuite& parameter) override; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestSuiteStart(const TestSuite& parameter) override; + void OnTestStart(const TestInfo& test_info) override; + void OnTestPartResult(const TestPartResult& result) override; + void OnTestEnd(const TestInfo& test_info) override; +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& parameter) override; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestSuiteEnd(const TestSuite& parameter) override; + void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override; + void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) override; + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void OnTestProgramEnd(const UnitTest& unit_test) override; + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + static_cast(i)); + return listener; + } + } + + return nullptr; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ + void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = listeners_.size(); i != 0; i--) { \ + listeners_[i - 1]->Name(parameter); \ + } \ + } \ + } + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestSuite) +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REPEATER_METHOD_(OnTestSuiteStart, TestSuite) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestSuite) +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REVERSE_REPEATER_METHOD_(OnTestSuiteEnd, TestSuite) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = listeners_.size(); i > 0; i--) { + listeners_[i - 1]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void ListTestsMatchingFilter(const std::vector& test_suites); + + // Prints an XML summary of all unit tests. + static void PrintXmlTestsList(std::ostream* stream, + const std::vector& test_suites); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams a test suite XML stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputXmlTestSuiteForTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams an XML representation of a TestResult object. + static void OutputXmlTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestSuite object + static void PrintXmlTestSuite(::std::ostream* stream, + const TestSuite& test_suite); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // Streams an XML representation of the test properties of a TestResult + // object. + static void OutputXmlTestProperties(std::ostream* stream, + const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "XML output file may not be null"; + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +void XmlUnitTestResultPrinter::ListTestsMatchingFilter( + const std::vector& test_suites) { + FILE* xmlout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintXmlTestsList(&stream, test_suites); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// GOOGLETEST_CM0009 DO NOT DELETE +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestSuite object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast(ms) * 1e-3); + return ss.str(); +} + +static bool PortableLocaltime(time_t seconds, struct tm* out) { +#if defined(_MSC_VER) + return localtime_s(out, &seconds) == 0; +#elif defined(__MINGW32__) || defined(__MINGW64__) + // MINGW provides neither localtime_r nor localtime_s, but uses + // Windows' localtime(), which has a thread-local tm buffer. + struct tm* tm_ptr = localtime(&seconds); // NOLINT + if (tm_ptr == nullptr) return false; + *out = *tm_ptr; + return true; +#elif defined(__STDC_LIB_EXT1__) + // Uses localtime_s when available as localtime_r is only available from + // C23 standard. + return localtime_s(&seconds, out) != nullptr; +#else + return localtime_r(&seconds, out) != nullptr; +#endif +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss.sss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec) + "." + + String::FormatIntWidthN(static_cast(ms % 1000), 3); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != nullptr) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedOutputAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Streams a test suite XML stanza containing the given test result. +void XmlUnitTestResultPrinter::OutputXmlTestSuiteForTestResult( + ::std::ostream* stream, const TestResult& result) { + // Output the boilerplate for a minimal test suite with one test. + *stream << " "; + + // Output the boilerplate for a minimal test case with a single test. + *stream << " \n"; +} + +// Prints an XML representation of a TestInfo object. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestsuite = "testcase"; + + if (test_info.is_in_another_shard()) { + return; + } + + *stream << " \n"; + return; + } + + OutputXmlAttribute(stream, kTestsuite, "status", + test_info.should_run() ? "run" : "notrun"); + OutputXmlAttribute(stream, kTestsuite, "result", + test_info.should_run() + ? (result.Skipped() ? "skipped" : "completed") + : "suppressed"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(result.elapsed_time())); + OutputXmlAttribute( + stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsIso8601(result.start_timestamp())); + OutputXmlAttribute(stream, kTestsuite, "classname", test_suite_name); + + OutputXmlTestResult(stream, result); +} + +void XmlUnitTestResultPrinter::OutputXmlTestResult(::std::ostream* stream, + const TestResult& result) { + int failures = 0; + int skips = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + if (++failures == 1 && skips == 0) { + *stream << ">\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string summary = location + "\n" + part.summary(); + *stream << " "; + const std::string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } else if (part.skipped()) { + if (++skips == 1 && failures == 0) { + *stream << ">\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string summary = location + "\n" + part.summary(); + *stream << " "; + const std::string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0 && skips == 0 && result.test_property_count() == 0) { + *stream << " />\n"; + } else { + if (failures == 0 && skips == 0) { + *stream << ">\n"; + } + OutputXmlTestProperties(stream, result); + *stream << " \n"; + } +} + +// Prints an XML representation of a TestSuite object +void XmlUnitTestResultPrinter::PrintXmlTestSuite(std::ostream* stream, + const TestSuite& test_suite) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_suite.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_suite.reportable_test_count())); + if (!GTEST_FLAG(list_tests)) { + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_suite.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_suite.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "skipped", + StreamableToString(test_suite.skipped_test_count())); + + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_suite.elapsed_time())); + OutputXmlAttribute( + stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsIso8601(test_suite.start_timestamp())); + *stream << TestPropertiesAsXmlAttributes(test_suite.ad_hoc_test_result()); + } + *stream << ">\n"; + for (int i = 0; i < test_suite.total_test_count(); ++i) { + if (test_suite.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) + PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i)); + } + + // If there was a test failure outside of one of the test suites (like in a + // test environment) include that in the output. + if (unit_test.ad_hoc_test_result().Failed()) { + OutputXmlTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); + } + + *stream << "\n"; +} + +void XmlUnitTestResultPrinter::PrintXmlTestsList( + std::ostream* stream, const std::vector& test_suites) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + int total_tests = 0; + for (auto test_suite : test_suites) { + total_tests += test_suite->total_test_count(); + } + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(total_tests)); + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (auto test_suite : test_suites) { + PrintXmlTestSuite(stream, *test_suite); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +void XmlUnitTestResultPrinter::OutputXmlTestProperties( + std::ostream* stream, const TestResult& result) { + const std::string kProperties = "properties"; + const std::string kProperty = "property"; + + if (result.test_property_count() <= 0) { + return; + } + + *stream << "<" << kProperties << ">\n"; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + *stream << "<" << kProperty; + *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\""; + *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\""; + *stream << "/>\n"; + } + *stream << "\n"; +} + +// End XmlUnitTestResultPrinter + +// This class generates an JSON output file. +class JsonUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit JsonUnitTestResultPrinter(const char* output_file); + + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + + // Prints an JSON summary of all unit tests. + static void PrintJsonTestList(::std::ostream* stream, + const std::vector& test_suites); + + private: + // Returns an JSON-escaped copy of the input string str. + static std::string EscapeJson(const std::string& str); + + //// Verifies that the given attribute belongs to the given element and + //// streams the attribute as JSON. + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma = true); + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma = true); + + // Streams a test suite JSON stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputJsonTestSuiteForTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams a JSON representation of a TestResult object. + static void OutputJsonTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams a JSON representation of a TestInfo object. + static void OutputJsonTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info); + + // Prints a JSON representation of a TestSuite object + static void PrintJsonTestSuite(::std::ostream* stream, + const TestSuite& test_suite); + + // Prints a JSON summary of unit_test to output stream out. + static void PrintJsonUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as + // a JSON dictionary. + static std::string TestPropertiesAsJson(const TestResult& result, + const std::string& indent); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(JsonUnitTestResultPrinter); +}; + +// Creates a new JsonUnitTestResultPrinter. +JsonUnitTestResultPrinter::JsonUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "JSON output file may not be null"; + } +} + +void JsonUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* jsonout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintJsonUnitTest(&stream, unit_test); + fprintf(jsonout, "%s", StringStreamToString(&stream).c_str()); + fclose(jsonout); +} + +// Returns an JSON-escaped copy of the input string str. +std::string JsonUnitTestResultPrinter::EscapeJson(const std::string& str) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '\\': + case '"': + case '/': + m << '\\' << ch; + break; + case '\b': + m << "\\b"; + break; + case '\t': + m << "\\t"; + break; + case '\n': + m << "\\n"; + break; + case '\f': + m << "\\f"; + break; + case '\r': + m << "\\r"; + break; + default: + if (ch < ' ') { + m << "\\u00" << String::FormatByte(static_cast(ch)); + } else { + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// The following routines generate an JSON representation of a UnitTest +// object. + +// Formats the given time in milliseconds as seconds. +static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast(ms) * 1e-3) << "s"; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the +// RFC3339 format, without the timezone information. +static std::string FormatEpochTimeInMillisAsRFC3339(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec) + "Z"; +} + +static inline std::string Indent(size_t width) { + return std::string(width, ' '); +} + +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma) { + const std::vector& allowed_names = + GetReservedOutputAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; + + *stream << indent << "\"" << name << "\": \"" << EscapeJson(value) << "\""; + if (comma) + *stream << ",\n"; +} + +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma) { + const std::vector& allowed_names = + GetReservedOutputAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; + + *stream << indent << "\"" << name << "\": " << StreamableToString(value); + if (comma) + *stream << ",\n"; +} + +// Streams a test suite JSON stanza containing the given test result. +void JsonUnitTestResultPrinter::OutputJsonTestSuiteForTestResult( + ::std::ostream* stream, const TestResult& result) { + // Output the boilerplate for a new test suite. + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, "testsuite", "name", "NonTestSuiteFailure", Indent(6)); + OutputJsonKey(stream, "testsuite", "tests", 1, Indent(6)); + if (!GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, "testsuite", "failures", 1, Indent(6)); + OutputJsonKey(stream, "testsuite", "disabled", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "skipped", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "errors", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), + Indent(6)); + OutputJsonKey(stream, "testsuite", "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + Indent(6)); + } + *stream << Indent(6) << "\"testsuite\": [\n"; + + // Output the boilerplate for a new test case. + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, "testcase", "name", "", Indent(10)); + OutputJsonKey(stream, "testcase", "status", "RUN", Indent(10)); + OutputJsonKey(stream, "testcase", "result", "COMPLETED", Indent(10)); + OutputJsonKey(stream, "testcase", "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + Indent(10)); + OutputJsonKey(stream, "testcase", "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), + Indent(10)); + OutputJsonKey(stream, "testcase", "classname", "", Indent(10), false); + *stream << TestPropertiesAsJson(result, Indent(10)); + + // Output the actual test result. + OutputJsonTestResult(stream, result); + + // Finish the test suite. + *stream << "\n" << Indent(6) << "]\n" << Indent(4) << "}"; +} + +// Prints a JSON representation of a TestInfo object. +void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestsuite = "testcase"; + const std::string kIndent = Indent(10); + + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, kTestsuite, "name", test_info.name(), kIndent); + + if (test_info.value_param() != nullptr) { + OutputJsonKey(stream, kTestsuite, "value_param", test_info.value_param(), + kIndent); + } + if (test_info.type_param() != nullptr) { + OutputJsonKey(stream, kTestsuite, "type_param", test_info.type_param(), + kIndent); + } + if (GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, kTestsuite, "file", test_info.file(), kIndent); + OutputJsonKey(stream, kTestsuite, "line", test_info.line(), kIndent, false); + *stream << "\n" << Indent(8) << "}"; + return; + } + + OutputJsonKey(stream, kTestsuite, "status", + test_info.should_run() ? "RUN" : "NOTRUN", kIndent); + OutputJsonKey(stream, kTestsuite, "result", + test_info.should_run() + ? (result.Skipped() ? "SKIPPED" : "COMPLETED") + : "SUPPRESSED", + kIndent); + OutputJsonKey(stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuite, "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), kIndent); + OutputJsonKey(stream, kTestsuite, "classname", test_suite_name, kIndent, + false); + *stream << TestPropertiesAsJson(result, kIndent); + + OutputJsonTestResult(stream, result); +} + +void JsonUnitTestResultPrinter::OutputJsonTestResult(::std::ostream* stream, + const TestResult& result) { + const std::string kIndent = Indent(10); + + int failures = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + *stream << ",\n"; + if (++failures == 1) { + *stream << kIndent << "\"" << "failures" << "\": [\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string message = EscapeJson(location + "\n" + part.message()); + *stream << kIndent << " {\n" + << kIndent << " \"failure\": \"" << message << "\",\n" + << kIndent << " \"type\": \"\"\n" + << kIndent << " }"; + } + } + + if (failures > 0) + *stream << "\n" << kIndent << "]"; + *stream << "\n" << Indent(8) << "}"; +} + +// Prints an JSON representation of a TestSuite object +void JsonUnitTestResultPrinter::PrintJsonTestSuite( + std::ostream* stream, const TestSuite& test_suite) { + const std::string kTestsuite = "testsuite"; + const std::string kIndent = Indent(6); + + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, kTestsuite, "name", test_suite.name(), kIndent); + OutputJsonKey(stream, kTestsuite, "tests", test_suite.reportable_test_count(), + kIndent); + if (!GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, kTestsuite, "failures", + test_suite.failed_test_count(), kIndent); + OutputJsonKey(stream, kTestsuite, "disabled", + test_suite.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuite, "errors", 0, kIndent); + OutputJsonKey( + stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsRFC3339(test_suite.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuite, "time", + FormatTimeInMillisAsDuration(test_suite.elapsed_time()), + kIndent, false); + *stream << TestPropertiesAsJson(test_suite.ad_hoc_test_result(), kIndent) + << ",\n"; + } + + *stream << kIndent << "\"" << kTestsuite << "\": [\n"; + + bool comma = false; + for (int i = 0; i < test_suite.total_test_count(); ++i) { + if (test_suite.GetTestInfo(i)->is_reportable()) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + OutputJsonTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); + } + } + *stream << "\n" << kIndent << "]\n" << Indent(4) << "}"; +} + +// Prints a JSON summary of unit_test to output stream out. +void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + + OutputJsonKey(stream, kTestsuites, "tests", unit_test.reportable_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "failures", unit_test.failed_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "disabled", + unit_test.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent); + if (GTEST_FLAG(shuffle)) { + OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(), + kIndent); + } + OutputJsonKey(stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsRFC3339(unit_test.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuites, "time", + FormatTimeInMillisAsDuration(unit_test.elapsed_time()), kIndent, + false); + + *stream << TestPropertiesAsJson(unit_test.ad_hoc_test_result(), kIndent) + << ",\n"; + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + bool comma = false; + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + PrintJsonTestSuite(stream, *unit_test.GetTestSuite(i)); + } + } + + // If there was a test failure outside of one of the test suites (like in a + // test environment) include that in the output. + if (unit_test.ad_hoc_test_result().Failed()) { + OutputJsonTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); + } + + *stream << "\n" << kIndent << "]\n" << "}\n"; +} + +void JsonUnitTestResultPrinter::PrintJsonTestList( + std::ostream* stream, const std::vector& test_suites) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + int total_tests = 0; + for (auto test_suite : test_suites) { + total_tests += test_suite->total_test_count(); + } + OutputJsonKey(stream, kTestsuites, "tests", total_tests, kIndent); + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + for (size_t i = 0; i < test_suites.size(); ++i) { + if (i != 0) { + *stream << ",\n"; + } + PrintJsonTestSuite(stream, *test_suites[i]); + } + + *stream << "\n" + << kIndent << "]\n" + << "}\n"; +} +// Produces a string representing the test properties in a result as +// a JSON dictionary. +std::string JsonUnitTestResultPrinter::TestPropertiesAsJson( + const TestResult& result, const std::string& indent) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << ",\n" << indent << "\"" << property.key() << "\": " + << "\"" << EscapeJson(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End JsonUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +std::string StreamingListener::UrlEncode(const char* str) { + std::string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = nullptr; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != nullptr; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// class OsStackTraceGetter + +const char* const OsStackTraceGetterInterface::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_) { +#if GTEST_HAS_ABSL + std::string result; + + if (max_depth <= 0) { + return result; + } + + max_depth = std::min(max_depth, kMaxStackTraceDepth); + + std::vector raw_stack(max_depth); + // Skips the frames requested by the caller, plus this function. + const int raw_stack_size = + absl::GetStackTrace(&raw_stack[0], max_depth, skip_count + 1); + + void* caller_frame = nullptr; + { + MutexLock lock(&mutex_); + caller_frame = caller_frame_; + } + + for (int i = 0; i < raw_stack_size; ++i) { + if (raw_stack[i] == caller_frame && + !GTEST_FLAG(show_internal_stack_frames)) { + // Add a marker to the trace and stop adding frames. + absl::StrAppend(&result, kElidedFramesMarker, "\n"); + break; + } + + char tmp[1024]; + const char* symbol = "(unknown)"; + if (absl::Symbolize(raw_stack[i], tmp, sizeof(tmp))) { + symbol = tmp; + } + + char line[1024]; + snprintf(line, sizeof(line), " %p: %s\n", raw_stack[i], symbol); + result += line; + } + + return result; + +#else // !GTEST_HAS_ABSL + static_cast(max_depth); + static_cast(skip_count); + return ""; +#endif // GTEST_HAS_ABSL +} + +void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) { +#if GTEST_HAS_ABSL + void* caller_frame = nullptr; + if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) { + caller_frame = nullptr; + } + + MutexLock lock(&mutex_); + caller_frame_ = caller_frame; +#endif // GTEST_HAS_ABSL +} + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath ? + premature_exit_filepath : "") { + // If a path to the premature-exit file is specified... + if (!premature_exit_filepath_.empty()) { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { +#if !defined GTEST_OS_ESP8266 + if (!premature_exit_filepath_.empty()) { + int retval = remove(premature_exit_filepath_.c_str()); + if (retval) { + GTEST_LOG_(ERROR) << "Failed to remove premature exit filepath \"" + << premature_exit_filepath_ << "\" with error " + << retval; + } + } +#endif + } + + private: + const std::string premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(nullptr), + default_xml_generator_(nullptr) {} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = nullptr; + else if (listener == default_xml_generator_) + default_xml_generator_ = nullptr; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != nullptr) Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != nullptr) Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // defined(__BORLANDC__) +} + +// Gets the number of successful test suites. +int UnitTest::successful_test_suite_count() const { + return impl()->successful_test_suite_count(); +} + +// Gets the number of failed test suites. +int UnitTest::failed_test_suite_count() const { + return impl()->failed_test_suite_count(); +} + +// Gets the number of all test suites. +int UnitTest::total_test_suite_count() const { + return impl()->total_test_suite_count(); +} + +// Gets the number of all test suites that contain at least one test +// that should run. +int UnitTest::test_suite_to_run_count() const { + return impl()->test_suite_to_run_count(); +} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_suite_count(); +} +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_suite_count(); +} +int UnitTest::total_test_case_count() const { + return impl()->total_test_suite_count(); +} +int UnitTest::test_case_to_run_count() const { + return impl()->test_suite_to_run_count(); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of skipped tests. +int UnitTest::skipped_test_count() const { + return impl()->skipped_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true if and only if the unit test passed (i.e. all test suites +// passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true if and only if the unit test failed (i.e. some test suite +// failed or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test suite among all the test suites. i can range from 0 to +// total_test_suite_count() - 1. If i is not in that range, returns NULL. +const TestSuite* UnitTest::GetTestSuite(int i) const { + return impl()->GetTestSuite(i); +} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test suites. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test suite among all the test suites. i can range from 0 to +// total_test_suite_count() - 1. If i is not in that range, returns NULL. +TestSuite* UnitTest::GetMutableTestSuite(int i) { + return impl()->GetMutableSuiteCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == nullptr) { + return nullptr; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (size_t i = impl_->gtest_trace_stack().size(); i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != nullptr && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = TestPartResult( + result_type, file_name, line_number, msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess && + result_type != TestPartResult::kSkip) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#elif (!defined(__native_client__)) && \ + ((defined(__clang__) || defined(__GNUC__)) && \ + (defined(__x86_64__) || defined(__i386__))) + // with clang/gcc we can achieve the same effect on x86 by invoking int3 + asm("int3"); +#else + // Dereference nullptr through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: some debuggers don't correctly trap abort(). + *static_cast(nullptr) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestSuite's ad_hoc_test_result_ when invoked +// from SetUpTestSuite or TearDownTestSuite, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process + ? nullptr + : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_OS_WINDOWS + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if defined(_MSC_VER) && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. + + // In debug mode, the Windows CRT can crash with an assertion over invalid + // input (e.g. passing an invalid file descriptor). The default handling + // for these assertions is to pop up a dialog and wait for user input. + // Instead ask the CRT to dump such assertions to stderr non-interactively. + if (!IsDebuggerPresent()) { + (void)_CrtSetReportMode(_CRT_ASSERT, + _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + (void)_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + } +# endif + } +#endif // GTEST_OS_WINDOWS + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestSuite object for the test that's currently running, +// or NULL if no test is running. +const TestSuite* UnitTest::current_test_suite() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_suite(); +} + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_suite(); +} +#endif + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +// Returns ParameterizedTestSuiteRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestSuiteRegistry& +UnitTest::parameterized_test_registry() GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */) + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), + GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), + parameterized_test_registry_(), + parameterized_tests_registered_(false), + last_death_test_suite_(-1), + current_test_suite_(nullptr), + current_test_info_(nullptr), + ad_hoc_test_result_(), + os_stack_trace_getter_(nullptr), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestSuite. + ForEach(test_suites_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test suite's ad_hoc_test_result when invoke +// from SetUpTestSuite/TearDownTestSuite, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != nullptr) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_suite_ != nullptr) { + xml_element = "testsuite"; + test_result = &(current_test_suite_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != nullptr) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format == "json") { + listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \"" + << output_format << "\" ignored."; + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + GTEST_LOG_(WARNING) << "unrecognized streaming target \"" << target + << "\" ignored."; + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + // Register to send notifications about key process state changes. + listeners()->Append(new GTEST_CUSTOM_TEST_EVENT_LISTENER_()); +#endif // defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + + if (GTEST_FLAG(brief)) { + listeners()->SetDefaultResultPrinter(new BriefUnitTestResultPrinter); + } + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + +#if GTEST_HAS_ABSL + if (GTEST_FLAG(install_failure_signal_handler)) { + absl::FailureSignalHandlerOptions options; + absl::InstallFailureSignalHandler(options); + } +#endif // GTEST_HAS_ABSL + } +} + +// A predicate that checks the name of a TestSuite against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestSuiteNameIs is copyable. +class TestSuiteNameIs { + public: + // Constructor. + explicit TestSuiteNameIs(const std::string& name) : name_(name) {} + + // Returns true if and only if the name of test_suite matches name_. + bool operator()(const TestSuite* test_suite) const { + return test_suite != nullptr && + strcmp(test_suite->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestSuite with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_suite_name: name of the test suite +// type_param: the name of the test suite's type parameter, or NULL if +// this is not a typed or a type-parameterized test suite. +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +TestSuite* UnitTestImpl::GetTestSuite( + const char* test_suite_name, const char* type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc) { + // Can we find a TestSuite with the given name? + const auto test_suite = + std::find_if(test_suites_.rbegin(), test_suites_.rend(), + TestSuiteNameIs(test_suite_name)); + + if (test_suite != test_suites_.rend()) return *test_suite; + + // No. Let's create one. + auto* const new_test_suite = + new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test suite? + if (internal::UnitTestOptions::MatchesFilter(test_suite_name, + kDeathTestSuiteFilter)) { + // Yes. Inserts the test suite after the last death test suite + // defined so far. This only works when the test suites haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_suite_; + test_suites_.insert(test_suites_.begin() + last_death_test_suite_, + new_test_suite); + } else { + // No. Appends to the end of the list. + test_suites_.push_back(new_test_suite); + } + + test_suite_indices_.push_back(static_cast(test_suite_indices_.size())); + return new_test_suite; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // True if and only if Google Test is initialized before RUN_ALL_TESTS() is + // called. + const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized(); + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True if and only if we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = + (internal_run_death_test_flag_.get() != nullptr); +# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) + if (in_subprocess_for_death_test) { + GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_(); + } +# endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True if and only if at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool gtest_repeat_forever = repeat < 0; + for (int i = 0; gtest_repeat_forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + Timer timer; + + // Shuffles test suites and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(static_cast(random_seed_)); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test suite if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure or skip triggered + // during global set-up. + if (Test::IsSkipped()) { + // Emit diagnostics when global set-up calls skip, as it will not be + // emitted by default. + TestResult& test_result = + *internal::GetUnitTestImpl()->current_test_result(); + for (int j = 0; j < test_result.total_part_count(); ++j) { + const TestPartResult& test_part_result = + test_result.GetTestPartResult(j); + if (test_part_result.type() == TestPartResult::kSkip) { + const std::string& result = test_part_result.message(); + printf("%s\n", result.c_str()); + } + } + fflush(stdout); + } else if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_suite_count(); + test_index++) { + GetMutableSuiteCase(test_index)->Run(); + if (GTEST_FLAG(fail_fast) && + GetMutableSuiteCase(test_index)->Failed()) { + for (int j = test_index + 1; j < total_test_suite_count(); j++) { + GetMutableSuiteCase(j)->Skip(); + } + break; + } + } + } else if (Test::HasFatalFailure()) { + // If there was a fatal failure during the global setup then we know we + // aren't going to run any tests. Explicitly mark all of the tests as + // skipped to make this obvious in the output. + for (int test_index = 0; test_index < total_test_suite_count(); + test_index++) { + GetMutableSuiteCase(test_index)->Skip(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = timer.Elapsed(); + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + if (!gtest_is_initialized_before_run_all_tests) { + ColoredPrintf( + GTestColor::kRed, + "\nIMPORTANT NOTICE - DO NOT IGNORE:\n" + "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_ + "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_ + " will start to enforce the valid usage. " + "Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT +#if GTEST_FOR_GOOGLE_ + ColoredPrintf(GTestColor::kRed, + "For more details, see http://wiki/Main/ValidGUnitMain.\n"); +#endif // GTEST_FOR_GOOGLE_ + } + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != nullptr) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == nullptr) { + ColoredPrintf(GTestColor::kRed, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const int32_t total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const int32_t shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +int32_t Int32FromEnvOrDie(const char* var, int32_t default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == nullptr) { + return default_val; + } + + int32_t result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true if and only if the test should be run on this shard. The test id +// is some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestSuite and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md +// . Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const int32_t total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const int32_t shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (auto* test_suite : test_suites_) { + const std::string& test_suite_name = test_suite->name(); + test_suite->set_should_run(false); + + for (size_t j = 0; j < test_suite->test_info_list().size(); j++) { + TestInfo* const test_info = test_suite->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test suite name or test name matches + // kDisableTestFilter. + const bool is_disabled = internal::UnitTestOptions::MatchesFilter( + test_suite_name, kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter( + test_name, kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = internal::UnitTestOptions::FilterMatchesTest( + test_suite_name, test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_in_another_shard = + shard_tests != IGNORE_SHARDING_PROTOCOL && + !ShouldRunTestOnShard(total_shards, shard_index, num_runnable_tests); + test_info->is_in_another_shard_ = is_in_another_shard; + const bool is_selected = is_runnable && !is_in_another_shard; + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_suite->set_should_run(test_suite->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != nullptr) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (auto* test_suite : test_suites_) { + bool printed_test_suite_name = false; + + for (size_t j = 0; j < test_suite->test_info_list().size(); j++) { + const TestInfo* const test_info = test_suite->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_suite_name) { + printed_test_suite_name = true; + printf("%s.", test_suite->name()); + if (test_suite->type_param() != nullptr) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_suite->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != nullptr) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml" || output_format == "json") { + FILE* fileout = OpenFileForWriting( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()); + std::stringstream stream; + if (output_format == "xml") { + XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) + .PrintXmlTestsList(&stream, test_suites_); + } else if (output_format == "json") { + JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) + .PrintJsonTestList(&stream, test_suites_); + } + fprintf(fileout, "%s", StringStreamToString(&stream).c_str()); + fclose(fileout); + } +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == nullptr) { +#ifdef GTEST_OS_STACK_TRACE_GETTER_ + os_stack_trace_getter_ = new GTEST_OS_STACK_TRACE_GETTER_; +#else + os_stack_trace_getter_ = new OsStackTraceGetter; +#endif // GTEST_OS_STACK_TRACE_GETTER_ + } + + return os_stack_trace_getter_; +} + +// Returns the most specific TestResult currently running. +TestResult* UnitTestImpl::current_test_result() { + if (current_test_info_ != nullptr) { + return ¤t_test_info_->result_; + } + if (current_test_suite_ != nullptr) { + return ¤t_test_suite_->ad_hoc_test_result_; + } + return &ad_hoc_test_result_; +} + +// Shuffles all test suites, and the tests within each test suite, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test suites. + ShuffleRange(random(), 0, last_death_test_suite_ + 1, &test_suite_indices_); + + // Shuffles the non-death test suites. + ShuffleRange(random(), last_death_test_suite_ + 1, + static_cast(test_suites_.size()), &test_suite_indices_); + + // Shuffles the tests inside each test suite. + for (auto& test_suite : test_suites_) { + test_suite->ShuffleTests(random()); + } +} + +// Restores the test suites and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_suites_.size(); i++) { + // Unshuffles the tests in each test suite. + test_suites_[i]->UnshuffleTests(); + // Resets the index of each test suite. + test_suite_indices_[i] = static_cast(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +static const char* ParseFlagValue(const char* str, const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == nullptr || flag == nullptr) return nullptr; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return nullptr; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an int32_t flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +template +static bool ParseStringFlag(const char* str, const char* flag, String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +static void PrintColorEncoded(const char* str) { + GTestColor color = GTestColor::kDefault; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == nullptr) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = GTestColor::kDefault; + } else if (ch == 'R') { + color = GTestColor::kRed; + } else if (ch == 'G') { + color = GTestColor::kGreen; + } else if (ch == 'Y') { + color = GTestColor::kYellow; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = + "This program contains tests written using " GTEST_NAME_ + ". You can use the\n" + "following command line flags to control its behavior:\n" + "\n" + "Test Selection:\n" + " @G--" GTEST_FLAG_PREFIX_ + "list_tests@D\n" + " List the names of all tests instead of running them. The name of\n" + " TEST(Foo, Bar) is \"Foo.Bar\".\n" + " @G--" GTEST_FLAG_PREFIX_ + "filter=@YPOSITIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" + " Run only the tests whose name matches one of the positive patterns " + "but\n" + " none of the negative patterns. '?' matches any single character; " + "'*'\n" + " matches any substring; ':' separates two patterns.\n" + " @G--" GTEST_FLAG_PREFIX_ + "also_run_disabled_tests@D\n" + " Run all disabled tests too.\n" + "\n" + "Test Execution:\n" + " @G--" GTEST_FLAG_PREFIX_ + "repeat=@Y[COUNT]@D\n" + " Run the tests repeatedly; use a negative count to repeat forever.\n" + " @G--" GTEST_FLAG_PREFIX_ + "shuffle@D\n" + " Randomize tests' orders on every iteration.\n" + " @G--" GTEST_FLAG_PREFIX_ + "random_seed=@Y[NUMBER]@D\n" + " Random number seed to use for shuffling test orders (between 1 and\n" + " 99999, or 0 to use a seed based on the current time).\n" + "\n" + "Test Output:\n" + " @G--" GTEST_FLAG_PREFIX_ + "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" + " Enable/disable colored output. The default is @Gauto@D.\n" + " @G--" GTEST_FLAG_PREFIX_ + "brief=1@D\n" + " Only print test failures.\n" + " @G--" GTEST_FLAG_PREFIX_ + "print_time=0@D\n" + " Don't print the elapsed time of each test.\n" + " @G--" GTEST_FLAG_PREFIX_ + "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G" GTEST_PATH_SEP_ + "@Y|@G:@YFILE_PATH]@D\n" + " Generate a JSON or XML report in the given directory or with the " + "given\n" + " file name. @YFILE_PATH@D defaults to @Gtest_detail.xml@D.\n" +# if GTEST_CAN_STREAM_RESULTS_ + " @G--" GTEST_FLAG_PREFIX_ + "stream_result_to=@YHOST@G:@YPORT@D\n" + " Stream test results to the given server.\n" +# endif // GTEST_CAN_STREAM_RESULTS_ + "\n" + "Assertion Behavior:\n" +# if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS + " @G--" GTEST_FLAG_PREFIX_ + "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" + " Set the default death test style.\n" +# endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS + " @G--" GTEST_FLAG_PREFIX_ + "break_on_failure@D\n" + " Turn assertion failures into debugger break-points.\n" + " @G--" GTEST_FLAG_PREFIX_ + "throw_on_failure@D\n" + " Turn assertion failures into C++ exceptions for use by an external\n" + " test framework.\n" + " @G--" GTEST_FLAG_PREFIX_ + "catch_exceptions=0@D\n" + " Do not report exceptions as test failures. Instead, allow them\n" + " to crash the program or throw a pop-up (on Windows).\n" + "\n" + "Except for @G--" GTEST_FLAG_PREFIX_ + "list_tests@D, you can alternatively set " + "the corresponding\n" + "environment variable of a flag (all letters in upper-case). For example, " + "to\n" + "disable colored text output, you can either specify " + "@G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" + "the @G" GTEST_FLAG_PREFIX_UPPER_ + "COLOR@D environment variable to @Gno@D.\n" + "\n" + "For more information, please read the " GTEST_NAME_ + " documentation at\n" + "@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ + "\n" + "(not one in your own code or tests), please report it to\n" + "@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +static bool ParseGoogleTestFlag(const char* const arg) { + return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseBoolFlag(arg, kFailFast, >EST_FLAG(fail_fast)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kBriefFlag, >EST_FLAG(brief)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseBoolFlag(arg, kPrintUTF8Flag, >EST_FLAG(print_utf8)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, >EST_FLAG(throw_on_failure)); +} + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +static void LoadFlagsFromFile(const std::string& path) { + FILE* flagfile = posix::FOpen(path.c_str(), "r"); + if (!flagfile) { + GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile) + << "\""; + } + std::string contents(ReadEntireFile(flagfile)); + posix::FClose(flagfile); + std::vector lines; + SplitString(contents, '\n', &lines); + for (size_t i = 0; i < lines.size(); ++i) { + if (lines[i].empty()) + continue; + if (!ParseGoogleTestFlag(lines[i].c_str())) + g_help_flag = true; + } +} +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + bool remove_flag = false; + if (ParseGoogleTestFlag(arg)) { + remove_flag = true; +#if GTEST_USE_OWN_FLAGFILE_FLAG_ + } else if (ParseStringFlag(arg, kFlagfileFlag, >EST_FLAG(flagfile))) { + LoadFlagsFromFile(GTEST_FLAG(flagfile)); + remove_flag = true; +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + + if (remove_flag) { + // Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); + + // Fix the value of *_NSGetArgc() on macOS, but if and only if + // *_NSGetArgv() == argv + // Only applicable to char** version of argv +#if GTEST_OS_MAC +#ifndef GTEST_OS_IOS + if (*_NSGetArgv() == argv) { + *_NSGetArgc() = *argc; + } +#endif +#endif +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + // We don't want to run the initialization code twice. + if (GTestIsInitialized()) return; + + if (*argc <= 0) return; + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#if GTEST_HAS_ABSL + absl::InitializeSymbolizer(g_argvs[0].c_str()); +#endif // GTEST_HAS_ABSL + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +void InitGoogleTest() { + // Since Arduino doesn't have a command line, fake out the argc/argv arguments + int argc = 1; + const auto arg0 = "dummy"; + char* argv0 = const_cast(arg0); + char** argv = &argv0; + +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(&argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(&argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +std::string TempDir() { +#if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) + return GTEST_CUSTOM_TEMPDIR_FUNCTION_(); +#elif GTEST_OS_WINDOWS_MOBILE + return "\\temp\\"; +#elif GTEST_OS_WINDOWS + const char* temp_dir = internal::posix::GetEnv("TEMP"); + if (temp_dir == nullptr || temp_dir[0] == '\0') { + return "\\temp\\"; + } else if (temp_dir[strlen(temp_dir) - 1] == '\\') { + return temp_dir; + } else { + return std::string(temp_dir) + "\\"; + } +#elif GTEST_OS_LINUX_ANDROID + const char* temp_dir = internal::posix::GetEnv("TEST_TMPDIR"); + if (temp_dir == nullptr || temp_dir[0] == '\0') { + return "/data/local/tmp/"; + } else { + return temp_dir; + } +#elif GTEST_OS_LINUX + const char* temp_dir = internal::posix::GetEnv("TEST_TMPDIR"); + if (temp_dir == nullptr || temp_dir[0] == '\0') { + return "/tmp/"; + } else { + return temp_dir; + } +#else + return "/tmp/"; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +void ScopedTrace::PushTrace(const char* file, int line, std::string message) { + internal::TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message.swap(message); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + +} // namespace testing +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// This file implements death tests. + + +#include +#include + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +# if GTEST_OS_FUCHSIA +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# endif // GTEST_OS_FUCHSIA + +#endif // GTEST_HAS_DEATH_TEST + + +namespace testing { + +// Constants. + +// The default death test style. +// +// This is defined in internal/gtest-port.h as "fast", but can be overridden by +// a definition in internal/custom/gtest-port.h. The recommended value, which is +// used internally at Google, is "threadsafe". +static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the " + "current process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +static bool g_in_fast_death_test_child = false; +# endif + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + // On Windows and Fuchsia, death tests are thread-safe regardless of the value + // of the death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA +} + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { +# if defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) + { + bool result; + if (GTEST_KILLED_BY_SIGNAL_OVERRIDE_(signum_, exit_status, &result)) { + return result; + } + } +# endif // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) { + msg << "couldn't detect the number of threads."; + } else { + msg << "detected " << thread_count << " threads."; + } + msg << " See " + "https://github.com/google/googletest/blob/master/docs/" + "advanced.md#death-tests-and-threads" + << " for more explanation and suggested solutions, especially if" + << " this is the last message you see before your test times out."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +#if GTEST_OS_FUCHSIA + +// File descriptor used for the pipe in the child process. +static const int kFuchsiaReadPipeFd = 3; + +#endif + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +static void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != nullptr) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == nullptr) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, + Matcher matcher, const char* file, + int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, std::move(matcher), file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, Matcher matcher) + : statement_(a_statement), + matcher_(std::move(matcher)), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() override { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason) override; + bool Passed(bool status_ok) override; + + const char* statement() const { return statement_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + // Returns stderr output from the child process. + virtual std::string GetErrorLogs(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // A matcher that's expected to match the stderr output by the child process. + Matcher matcher_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +std::string DeathTestImpl::GetErrorLogs() { + return GetCapturedStderr(); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// matcher_: A matcher that's expected to match the stderr output by the child +// process. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true if and only if all of the above conditions are met. Otherwise, +// the first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetErrorLogs(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + if (matcher_.Matches(error_message)) { + success = true; + } else { + std::ostringstream stream; + matcher_.DescribeTo(&stream); + buffer << " Result: died but not with expected error.\n" + << " Expected: " << stream.str() << "\n" + << "Actual msg:\n" + << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, Matcher matcher, + const char* file, int line) + : DeathTestImpl(a_statement, std::move(matcher)), + file_(file), + line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != nullptr) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = {sizeof(SECURITY_ATTRIBUTES), + nullptr, TRUE}; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + nullptr)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != nullptr); + const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ + + kFilterFlag + "=" + info->test_suite_name() + + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_(_MAX_PATH + 1 != ::GetModuleFileNameA(nullptr, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_( + ::CreateProcessA( + executable_path, const_cast(command_line.c_str()), + nullptr, // Retuned process handle is not inheritable. + nullptr, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + nullptr, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} + +# elif GTEST_OS_FUCHSIA + +class FuchsiaDeathTest : public DeathTestImpl { + public: + FuchsiaDeathTest(const char* a_statement, Matcher matcher, + const char* file, int line) + : DeathTestImpl(a_statement, std::move(matcher)), + file_(file), + line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + int Wait() override; + TestRole AssumeRole() override; + std::string GetErrorLogs() override; + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // The stderr data captured by the child process. + std::string captured_stderr_; + + zx::process child_process_; + zx::channel exception_channel_; + zx::socket stderr_socket_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { args_.push_back(nullptr); } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + int size() { + return static_cast(args_.size()) - 1; + } + + private: + std::vector args_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int FuchsiaDeathTest::Wait() { + const int kProcessKey = 0; + const int kSocketKey = 1; + const int kExceptionKey = 2; + + if (!spawned()) + return 0; + + // Create a port to wait for socket/task/exception events. + zx_status_t status_zx; + zx::port port; + status_zx = zx::port::create(0, &port); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Register to wait for the child process to terminate. + status_zx = child_process_.wait_async( + port, kProcessKey, ZX_PROCESS_TERMINATED, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Register to wait for the socket to be readable or closed. + status_zx = stderr_socket_.wait_async( + port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Register to wait for an exception. + status_zx = exception_channel_.wait_async( + port, kExceptionKey, ZX_CHANNEL_READABLE, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + bool process_terminated = false; + bool socket_closed = false; + do { + zx_port_packet_t packet = {}; + status_zx = port.wait(zx::time::infinite(), &packet); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + if (packet.key == kExceptionKey) { + // Process encountered an exception. Kill it directly rather than + // letting other handlers process the event. We will get a kProcessKey + // event when the process actually terminates. + status_zx = child_process_.kill(); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + } else if (packet.key == kProcessKey) { + // Process terminated. + GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); + GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED); + process_terminated = true; + } else if (packet.key == kSocketKey) { + GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); + if (packet.signal.observed & ZX_SOCKET_READABLE) { + // Read data from the socket. + constexpr size_t kBufferSize = 1024; + do { + size_t old_length = captured_stderr_.length(); + size_t bytes_read = 0; + captured_stderr_.resize(old_length + kBufferSize); + status_zx = stderr_socket_.read( + 0, &captured_stderr_.front() + old_length, kBufferSize, + &bytes_read); + captured_stderr_.resize(old_length + bytes_read); + } while (status_zx == ZX_OK); + if (status_zx == ZX_ERR_PEER_CLOSED) { + socket_closed = true; + } else { + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_ERR_SHOULD_WAIT); + status_zx = stderr_socket_.wait_async( + port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + } + } else { + GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_SOCKET_PEER_CLOSED); + socket_closed = true; + } + } + } while (!process_terminated && !socket_closed); + + ReadAndInterpretStatusByte(); + + zx_info_process_t buffer; + status_zx = child_process_.get_info(ZX_INFO_PROCESS, &buffer, sizeof(buffer), + nullptr, nullptr); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + GTEST_DEATH_TEST_CHECK_(buffer.flags & ZX_INFO_PROCESS_FLAG_EXITED); + set_status(static_cast(buffer.return_code)); + return status(); +} + +// The AssumeRole process for a Fuchsia death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole FuchsiaDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != nullptr) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(kFuchsiaReadPipeFd); + return EXECUTE_TEST; + } + + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // Build the child process command line. + const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ + + kFilterFlag + "=" + info->test_suite_name() + + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + + StreamableToString(line_) + "|" + + StreamableToString(death_test_index); + Arguments args; + args.AddArguments(GetInjectableArgvs()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + // Build the pipe for communication with the child. + zx_status_t status; + zx_handle_t child_pipe_handle; + int child_pipe_fd; + status = fdio_pipe_half(&child_pipe_fd, &child_pipe_handle); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + set_read_fd(child_pipe_fd); + + // Set the pipe handle for the child. + fdio_spawn_action_t spawn_actions[2] = {}; + fdio_spawn_action_t* add_handle_action = &spawn_actions[0]; + add_handle_action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; + add_handle_action->h.id = PA_HND(PA_FD, kFuchsiaReadPipeFd); + add_handle_action->h.handle = child_pipe_handle; + + // Create a socket pair will be used to receive the child process' stderr. + zx::socket stderr_producer_socket; + status = + zx::socket::create(0, &stderr_producer_socket, &stderr_socket_); + GTEST_DEATH_TEST_CHECK_(status >= 0); + int stderr_producer_fd = -1; + status = + fdio_fd_create(stderr_producer_socket.release(), &stderr_producer_fd); + GTEST_DEATH_TEST_CHECK_(status >= 0); + + // Make the stderr socket nonblocking. + GTEST_DEATH_TEST_CHECK_(fcntl(stderr_producer_fd, F_SETFL, 0) == 0); + + fdio_spawn_action_t* add_stderr_action = &spawn_actions[1]; + add_stderr_action->action = FDIO_SPAWN_ACTION_CLONE_FD; + add_stderr_action->fd.local_fd = stderr_producer_fd; + add_stderr_action->fd.target_fd = STDERR_FILENO; + + // Create a child job. + zx_handle_t child_job = ZX_HANDLE_INVALID; + status = zx_job_create(zx_job_default(), 0, & child_job); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + zx_policy_basic_t policy; + policy.condition = ZX_POL_NEW_ANY; + policy.policy = ZX_POL_ACTION_ALLOW; + status = zx_job_set_policy( + child_job, ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC, &policy, 1); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + // Create an exception channel attached to the |child_job|, to allow + // us to suppress the system default exception handler from firing. + status = + zx_task_create_exception_channel( + child_job, 0, exception_channel_.reset_and_get_address()); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + // Spawn the child process. + status = fdio_spawn_etc( + child_job, FDIO_SPAWN_CLONE_ALL, args.Argv()[0], args.Argv(), nullptr, + 2, spawn_actions, child_process_.reset_and_get_address(), nullptr); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + set_spawned(true); + return OVERSEE_TEST; +} + +std::string FuchsiaDeathTest::GetErrorLogs() { + return captured_stderr_; +} + +#else // We are neither on Windows, nor on Fuchsia. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, Matcher matcher); + + // All of these virtual functions are inherited from DeathTest. + int Wait() override; + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, + Matcher matcher) + : DeathTestImpl(a_statement, std::move(matcher)), child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, Matcher matcher) + : ForkingDeathTest(a_statement, std::move(matcher)) {} + TestRole AssumeRole() override; +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, Matcher matcher, + const char* file, int line) + : ForkingDeathTest(a_statement, std::move(matcher)), + file_(file), + line_(line) {} + TestRole AssumeRole() override; + + private: + static ::std::vector GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); +# if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) + ::std::vector extra_args = + GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_(); + args.insert(args.end(), extra_args.begin(), extra_args.end()); +# endif // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { args_.push_back(nullptr); } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_QNX +extern "C" char** environ; +# else // GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execv() as it's almost a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execv() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execv(args->argv[0], args->argv); + DeathTestAbort(std::string("execv(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // GTEST_OS_QNX + +# if GTEST_HAS_CLONE +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +static void StackLowerThanAddress(const void* ptr, + bool* result) GTEST_NO_INLINE_; +// Make sure sanitizers do not tamper with the stack here. +// Ideally, we want to use `__builtin_frame_address` instead of a local variable +// address with sanitizer disabled, but it does not work when the +// compiler optimizes the stack frame out, which happens on PowerPC targets. +// HWAddressSanitizer add a random tag to the MSB of the local variable address, +// making comparison result unpredictable. +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +static void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy = 0; + *result = std::less()(&dummy, ptr); +} + +// Make sure AddressSanitizer does not tamper with the stack here. +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +static bool StackGrowsDown() { + int dummy = 0; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} +# endif // GTEST_HAS_CLONE + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, nullptr, &inherit, args.argv, environ); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const auto stack_size = static_cast(getpagesize() * 2); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(nullptr, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_( + static_cast(stack_size) > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, nullptr)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != nullptr) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ + + kFilterFlag + "=" + info->test_suite_name() + + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, + Matcher matcher, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != nullptr) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = nullptr; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, std::move(matcher), file, line); + } + +# elif GTEST_OS_FUCHSIA + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new FuchsiaDeathTest(statement, std::move(matcher), file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, std::move(matcher), file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, std::move(matcher)); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +static int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return nullptr; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); + +# elif GTEST_OS_FUCHSIA + + if (fields.size() != 3 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \ + GTEST_OS_XTENSA + // These platforms do not have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + char* result = getcwd(cwd, sizeof(cwd)); +# if GTEST_OS_NACL + // getcwd will likely fail in NaCl due to the sandbox, so return something + // reasonable. The user may have provided a shim implementation for getcwd, + // however, so fallback only when failure is detected. + return FilePath(result == nullptr ? kCurrentDirectoryString : cwd); +# endif // GTEST_OS_NACL + return FilePath(result == nullptr ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurrence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != nullptr && + (last_sep == nullptr || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), static_cast(last_sep + 1 - c_str())); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat{}; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat{}; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, nullptr) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA + // do nothing + int result = 0; +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +void FilePath::Normalize() { + auto out = pathname_.begin(); + + for (const char character : pathname_) { + if (!IsPathSeparator(character)) { + *(out++) = character; + } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) { + *(out++) = kPathSeparator; + } else { + continue; + } + } + + pathname_.erase(out, pathname_.end()); +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + + +#include + +namespace testing { + +// Constructs a matcher that matches a const std::string& whose value is +// equal to s. +Matcher::Matcher(const std::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a const std::string& whose value is +// equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a std::string whose value is equal to +// s. +Matcher::Matcher(const std::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a std::string whose value is equal to +// s. +Matcher::Matcher(const char* s) { *this = Eq(std::string(s)); } + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Constructs a matcher that matches a const StringView& whose value is +// equal to s. +Matcher::Matcher(const std::string& s) { + *this = Eq(s); +} + +// Constructs a matcher that matches a const StringView& whose value is +// equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a const StringView& whose value is +// equal to s. +Matcher::Matcher(internal::StringView s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a StringView whose value is equal to +// s. +Matcher::Matcher(const std::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a StringView whose value is equal to +// s. +Matcher::Matcher(const char* s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a StringView whose value is equal to +// s. +Matcher::Matcher(internal::StringView s) { + *this = Eq(std::string(s)); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +#include +#include +#include +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS +# include +# include +# include +# include // Used in ThreadLocal. +# ifdef _MSC_VER +# include +# endif // _MSC_VER +#else +# include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \ + GTEST_OS_NETBSD || GTEST_OS_OPENBSD +# include +# if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD +# include +# endif +#endif + +#if GTEST_OS_QNX +# include +# include +# include +#endif // GTEST_OS_QNX + +#if GTEST_OS_AIX +# include +# include +#endif // GTEST_OS_AIX + +#if GTEST_OS_FUCHSIA +# include +# include +#endif // GTEST_OS_FUCHSIA + + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_LINUX + +namespace { +template +T ReadProcFileField(const std::string& filename, int field) { + std::string dummy; + std::ifstream file(filename.c_str()); + while (field-- > 0) { + file >> dummy; + } + T output = 0; + file >> output; + return output; +} +} // namespace + +// Returns the number of active threads, or 0 when there is an error. +size_t GetThreadCount() { + const std::string filename = + (Message() << "/proc/" << getpid() << "/stat").GetString(); + return ReadProcFileField(filename, 19); +} + +#elif GTEST_OS_MAC + +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \ + GTEST_OS_NETBSD + +#if GTEST_OS_NETBSD +#undef KERN_PROC +#define KERN_PROC KERN_PROC2 +#define kinfo_proc kinfo_proc2 +#endif + +#if GTEST_OS_DRAGONFLY +#define KP_NLWP(kp) (kp.kp_nthreads) +#elif GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD +#define KP_NLWP(kp) (kp.ki_numthreads) +#elif GTEST_OS_NETBSD +#define KP_NLWP(kp) (kp.p_nlwps) +#endif + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid(), +#if GTEST_OS_NETBSD + sizeof(struct kinfo_proc), + 1, +#endif + }; + u_int miblen = sizeof(mib) / sizeof(mib[0]); + struct kinfo_proc info; + size_t size = sizeof(info); + if (sysctl(mib, miblen, &info, &size, NULL, 0)) { + return 0; + } + return static_cast(KP_NLWP(info)); +} +#elif GTEST_OS_OPENBSD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID | KERN_PROC_SHOW_THREADS, + getpid(), + sizeof(struct kinfo_proc), + 0, + }; + u_int miblen = sizeof(mib) / sizeof(mib[0]); + + // get number of structs + size_t size; + if (sysctl(mib, miblen, NULL, &size, NULL, 0)) { + return 0; + } + + mib[5] = static_cast(size / static_cast(mib[4])); + + // populate array of structs + struct kinfo_proc info[mib[5]]; + if (sysctl(mib, miblen, &info, &size, NULL, 0)) { + return 0; + } + + // exclude empty members + size_t nthreads = 0; + for (size_t i = 0; i < size / static_cast(mib[4]); i++) { + if (info[i].p_tid != -1) + nthreads++; + } + return nthreads; +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), nullptr); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#elif GTEST_OS_AIX + +size_t GetThreadCount() { + struct procentry64 entry; + pid_t pid = getpid(); + int status = getprocs64(&entry, sizeof(entry), nullptr, 0, &pid, 1); + if (status == 1) { + return entry.pi_thcount; + } else { + return 0; + } +} + +#elif GTEST_OS_FUCHSIA + +size_t GetThreadCount() { + int dummy_buffer; + size_t avail; + zx_status_t status = zx_object_get_info( + zx_process_self(), + ZX_INFO_PROCESS_THREADS, + &dummy_buffer, + 0, + nullptr, + &avail); + if (status == ZX_OK) { + return avail; + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_LINUX + +#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +void SleepMilliseconds(int n) { + ::Sleep(static_cast(n)); +} + +AutoHandle::AutoHandle() + : handle_(INVALID_HANDLE_VALUE) {} + +AutoHandle::AutoHandle(Handle handle) + : handle_(handle) {} + +AutoHandle::~AutoHandle() { + Reset(); +} + +AutoHandle::Handle AutoHandle::Get() const { + return handle_; +} + +void AutoHandle::Reset() { + Reset(INVALID_HANDLE_VALUE); +} + +void AutoHandle::Reset(HANDLE handle) { + // Resetting with the same handle we already own is invalid. + if (handle_ != handle) { + if (IsCloseable()) { + ::CloseHandle(handle_); + } + handle_ = handle; + } else { + GTEST_CHECK_(!IsCloseable()) + << "Resetting a valid handle to itself is likely a programmer error " + "and thus not allowed."; + } +} + +bool AutoHandle::IsCloseable() const { + // Different Windows APIs may use either of these values to represent an + // invalid handle. + return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE; +} + +Notification::Notification() + : event_(::CreateEvent(nullptr, // Default security attributes. + TRUE, // Do not reset automatically. + FALSE, // Initially unset. + nullptr)) { // Anonymous event. + GTEST_CHECK_(event_.Get() != nullptr); +} + +void Notification::Notify() { + GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE); +} + +void Notification::WaitForNotification() { + GTEST_CHECK_( + ::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0); +} + +Mutex::Mutex() + : owner_thread_id_(0), + type_(kDynamic), + critical_section_init_phase_(0), + critical_section_(new CRITICAL_SECTION) { + ::InitializeCriticalSection(critical_section_); +} + +Mutex::~Mutex() { + // Static mutexes are leaked intentionally. It is not thread-safe to try + // to clean them up. + if (type_ == kDynamic) { + ::DeleteCriticalSection(critical_section_); + delete critical_section_; + critical_section_ = nullptr; + } +} + +void Mutex::Lock() { + ThreadSafeLazyInit(); + ::EnterCriticalSection(critical_section_); + owner_thread_id_ = ::GetCurrentThreadId(); +} + +void Mutex::Unlock() { + ThreadSafeLazyInit(); + // We don't protect writing to owner_thread_id_ here, as it's the + // caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + owner_thread_id_ = 0; + ::LeaveCriticalSection(critical_section_); +} + +// Does nothing if the current thread holds the mutex. Otherwise, crashes +// with high probability. +void Mutex::AssertHeld() { + ThreadSafeLazyInit(); + GTEST_CHECK_(owner_thread_id_ == ::GetCurrentThreadId()) + << "The current thread is not holding the mutex @" << this; +} + +namespace { + +#ifdef _MSC_VER +// Use the RAII idiom to flag mem allocs that are intentionally never +// deallocated. The motivation is to silence the false positive mem leaks +// that are reported by the debug version of MS's CRT which can only detect +// if an alloc is missing a matching deallocation. +// Example: +// MemoryIsNotDeallocated memory_is_not_deallocated; +// critical_section_ = new CRITICAL_SECTION; +// +class MemoryIsNotDeallocated +{ + public: + MemoryIsNotDeallocated() : old_crtdbg_flag_(0) { + old_crtdbg_flag_ = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + // Set heap allocation block type to _IGNORE_BLOCK so that MS debug CRT + // doesn't report mem leak if there's no matching deallocation. + _CrtSetDbgFlag(old_crtdbg_flag_ & ~_CRTDBG_ALLOC_MEM_DF); + } + + ~MemoryIsNotDeallocated() { + // Restore the original _CRTDBG_ALLOC_MEM_DF flag + _CrtSetDbgFlag(old_crtdbg_flag_); + } + + private: + int old_crtdbg_flag_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MemoryIsNotDeallocated); +}; +#endif // _MSC_VER + +} // namespace + +// Initializes owner_thread_id_ and critical_section_ in static mutexes. +void Mutex::ThreadSafeLazyInit() { + // Dynamic mutexes are initialized in the constructor. + if (type_ == kStatic) { + switch ( + ::InterlockedCompareExchange(&critical_section_init_phase_, 1L, 0L)) { + case 0: + // If critical_section_init_phase_ was 0 before the exchange, we + // are the first to test it and need to perform the initialization. + owner_thread_id_ = 0; + { + // Use RAII to flag that following mem alloc is never deallocated. +#ifdef _MSC_VER + MemoryIsNotDeallocated memory_is_not_deallocated; +#endif // _MSC_VER + critical_section_ = new CRITICAL_SECTION; + } + ::InitializeCriticalSection(critical_section_); + // Updates the critical_section_init_phase_ to 2 to signal + // initialization complete. + GTEST_CHECK_(::InterlockedCompareExchange( + &critical_section_init_phase_, 2L, 1L) == + 1L); + break; + case 1: + // Somebody else is already initializing the mutex; spin until they + // are done. + while (::InterlockedCompareExchange(&critical_section_init_phase_, + 2L, + 2L) != 2L) { + // Possibly yields the rest of the thread's time slice to other + // threads. + ::Sleep(0); + } + break; + + case 2: + break; // The mutex is already initialized and ready for use. + + default: + GTEST_CHECK_(false) + << "Unexpected value of critical_section_init_phase_ " + << "while initializing a static mutex."; + } + } +} + +namespace { + +class ThreadWithParamSupport : public ThreadWithParamBase { + public: + static HANDLE CreateThread(Runnable* runnable, + Notification* thread_can_start) { + ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start); + DWORD thread_id; + HANDLE thread_handle = ::CreateThread( + nullptr, // Default security. + 0, // Default stack size. + &ThreadWithParamSupport::ThreadMain, + param, // Parameter to ThreadMainStatic + 0x0, // Default creation flags. + &thread_id); // Need a valid pointer for the call to work under Win98. + GTEST_CHECK_(thread_handle != nullptr) + << "CreateThread failed with error " << ::GetLastError() << "."; + if (thread_handle == nullptr) { + delete param; + } + return thread_handle; + } + + private: + struct ThreadMainParam { + ThreadMainParam(Runnable* runnable, Notification* thread_can_start) + : runnable_(runnable), + thread_can_start_(thread_can_start) { + } + std::unique_ptr runnable_; + // Does not own. + Notification* thread_can_start_; + }; + + static DWORD WINAPI ThreadMain(void* ptr) { + // Transfers ownership. + std::unique_ptr param(static_cast(ptr)); + if (param->thread_can_start_ != nullptr) + param->thread_can_start_->WaitForNotification(); + param->runnable_->Run(); + return 0; + } + + // Prohibit instantiation. + ThreadWithParamSupport(); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParamSupport); +}; + +} // namespace + +ThreadWithParamBase::ThreadWithParamBase(Runnable *runnable, + Notification* thread_can_start) + : thread_(ThreadWithParamSupport::CreateThread(runnable, + thread_can_start)) { +} + +ThreadWithParamBase::~ThreadWithParamBase() { + Join(); +} + +void ThreadWithParamBase::Join() { + GTEST_CHECK_(::WaitForSingleObject(thread_.Get(), INFINITE) == WAIT_OBJECT_0) + << "Failed to join the thread with error " << ::GetLastError() << "."; +} + +// Maps a thread to a set of ThreadIdToThreadLocals that have values +// instantiated on that thread and notifies them when the thread exits. A +// ThreadLocal instance is expected to persist until all threads it has +// values on have terminated. +class ThreadLocalRegistryImpl { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { +#ifdef _MSC_VER + MemoryIsNotDeallocated memory_is_not_deallocated; +#endif // _MSC_VER + DWORD current_thread = ::GetCurrentThreadId(); + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(current_thread); + if (thread_local_pos == thread_to_thread_locals->end()) { + thread_local_pos = thread_to_thread_locals->insert( + std::make_pair(current_thread, ThreadLocalValues())).first; + StartWatcherThreadFor(current_thread); + } + ThreadLocalValues& thread_local_values = thread_local_pos->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos == thread_local_values.end()) { + value_pos = + thread_local_values + .insert(std::make_pair( + thread_local_instance, + std::shared_ptr( + thread_local_instance->NewValueForCurrentThread()))) + .first; + } + return value_pos->second.get(); + } + + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + std::vector > value_holders; + // Clean up the ThreadLocalValues data structure while holding the lock, but + // defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + for (ThreadIdToThreadLocals::iterator it = + thread_to_thread_locals->begin(); + it != thread_to_thread_locals->end(); + ++it) { + ThreadLocalValues& thread_local_values = it->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos != thread_local_values.end()) { + value_holders.push_back(value_pos->second); + thread_local_values.erase(value_pos); + // This 'if' can only be successful at most once, so theoretically we + // could break out of the loop here, but we don't bother doing so. + } + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + static void OnThreadExit(DWORD thread_id) { + GTEST_CHECK_(thread_id != 0) << ::GetLastError(); + std::vector > value_holders; + // Clean up the ThreadIdToThreadLocals data structure while holding the + // lock, but defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(thread_id); + if (thread_local_pos != thread_to_thread_locals->end()) { + ThreadLocalValues& thread_local_values = thread_local_pos->second; + for (ThreadLocalValues::iterator value_pos = + thread_local_values.begin(); + value_pos != thread_local_values.end(); + ++value_pos) { + value_holders.push_back(value_pos->second); + } + thread_to_thread_locals->erase(thread_local_pos); + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + private: + // In a particular thread, maps a ThreadLocal object to its value. + typedef std::map > + ThreadLocalValues; + // Stores all ThreadIdToThreadLocals having values in a thread, indexed by + // thread's ID. + typedef std::map ThreadIdToThreadLocals; + + // Holds the thread id and thread handle that we pass from + // StartWatcherThreadFor to WatcherThreadFunc. + typedef std::pair ThreadIdAndHandle; + + static void StartWatcherThreadFor(DWORD thread_id) { + // The returned handle will be kept in thread_map and closed by + // watcher_thread in WatcherThreadFunc. + HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, + FALSE, + thread_id); + GTEST_CHECK_(thread != nullptr); + // We need to pass a valid thread ID pointer into CreateThread for it + // to work correctly under Win98. + DWORD watcher_thread_id; + HANDLE watcher_thread = ::CreateThread( + nullptr, // Default security. + 0, // Default stack size + &ThreadLocalRegistryImpl::WatcherThreadFunc, + reinterpret_cast(new ThreadIdAndHandle(thread_id, thread)), + CREATE_SUSPENDED, &watcher_thread_id); + GTEST_CHECK_(watcher_thread != nullptr); + // Give the watcher thread the same priority as ours to avoid being + // blocked by it. + ::SetThreadPriority(watcher_thread, + ::GetThreadPriority(::GetCurrentThread())); + ::ResumeThread(watcher_thread); + ::CloseHandle(watcher_thread); + } + + // Monitors exit from a given thread and notifies those + // ThreadIdToThreadLocals about thread termination. + static DWORD WINAPI WatcherThreadFunc(LPVOID param) { + const ThreadIdAndHandle* tah = + reinterpret_cast(param); + GTEST_CHECK_( + ::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0); + OnThreadExit(tah->first); + ::CloseHandle(tah->second); + delete tah; + return 0; + } + + // Returns map of thread local instances. + static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() { + mutex_.AssertHeld(); +#ifdef _MSC_VER + MemoryIsNotDeallocated memory_is_not_deallocated; +#endif // _MSC_VER + static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals(); + return map; + } + + // Protects access to GetThreadLocalsMapLocked() and its return value. + static Mutex mutex_; + // Protects access to GetThreadMapLocked() and its return value. + static Mutex thread_map_mutex_; +}; + +Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex); // NOLINT +Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex); // NOLINT + +ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { + return ThreadLocalRegistryImpl::GetValueOnCurrentThread( + thread_local_instance); +} + +void ThreadLocalRegistry::OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + ThreadLocalRegistryImpl::OnThreadLocalDestroyed(thread_local_instance); +} + +#endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true if and only if regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true if and only if regular expression re matches a substring of +// str (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true if and only if ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != nullptr; +} + +// Returns true if and only if ch belongs to the given classification. +// Unlike similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true if and only if "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true if and only if the given atom (specified by escaped and +// pattern) matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +static std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == nullptr) { + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True if and only if ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true if and only if regex matches a prefix of str. regex must +// be a valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true if and only if regex matches any substring of str. regex must +// be a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == nullptr || str == nullptr) return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true if and only if regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true if and only if regular expression re matches a substring of +// str (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = nullptr; + if (regex != nullptr) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == nullptr ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == nullptr ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} + +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in a temporary directory. + std::string name_template; + +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /data/local/tmp is directly accessible from native code. + // '/sdcard' and other variants cannot be relied on, as they are not + // guaranteed to be mounted, or may have a delay in mounting. + name_template = "/data/local/tmp/"; +# elif GTEST_OS_IOS + char user_temp_dir[PATH_MAX + 1]; + + // Documented alternative to NSTemporaryDirectory() (for obtaining creating + // a temporary directory) at + // https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html#//apple_ref/doc/uid/TP40002585-SW10 + // + // _CS_DARWIN_USER_TEMP_DIR (as well as _CS_DARWIN_USER_CACHE_DIR) is not + // documented in the confstr() man page at + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/confstr.3.html#//apple_ref/doc/man/3/confstr + // but are still available, according to the WebKit patches at + // https://trac.webkit.org/changeset/262004/webkit + // https://trac.webkit.org/changeset/263705/webkit + // + // The confstr() implementation falls back to getenv("TMPDIR"). See + // https://opensource.apple.com/source/Libc/Libc-1439.100.3/gen/confstr.c.auto.html + ::confstr(_CS_DARWIN_USER_TEMP_DIR, user_temp_dir, sizeof(user_temp_dir)); + + name_template = user_temp_dir; + if (name_template.back() != GTEST_PATH_SEP_[0]) + name_template.push_back(GTEST_PATH_SEP_[0]); +# else + name_template = "/tmp/"; +# endif + name_template.append("gtest_captured_stream.XXXXXX"); + + // mkstemp() modifies the string bytes in place, and does not go beyond the + // string's length. This results in well-defined behavior in C++17. + // + // The const_cast is needed below C++17. The constraints on std::string + // implementations in C++11 and above make assumption behind the const_cast + // fairly safe. + const int captured_fd = ::mkstemp(const_cast(name_template.data())); + if (captured_fd == -1) { + GTEST_LOG_(WARNING) + << "Failed to create tmp file " << name_template + << " for test; does the test have access to the /tmp directory?"; + } + filename_ = std::move(name_template); +# endif // GTEST_OS_WINDOWS + fflush(nullptr); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(nullptr); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + if (file == nullptr) { + GTEST_LOG_(FATAL) << "Failed to open tmp file " << filename_ + << " for capturing stream."; + } + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +static CapturedStream* g_captured_stderr = nullptr; +static CapturedStream* g_captured_stdout = nullptr; + +// Starts capturing an output stream (stdout/stderr). +static void CaptureStream(int fd, const char* stream_name, + CapturedStream** stream) { + if (*stream != nullptr) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +static std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = nullptr; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + + + + + +size_t GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +std::string ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +#if GTEST_HAS_DEATH_TEST +static const std::vector* g_injected_test_argvs = + nullptr; // Owned. + +std::vector GetInjectableArgvs() { + if (g_injected_test_argvs != nullptr) { + return *g_injected_test_argvs; + } + return GetArgvs(); +} + +void SetInjectableArgvs(const std::vector* new_argvs) { + if (g_injected_test_argvs != new_argvs) delete g_injected_test_argvs; + g_injected_test_argvs = new_argvs; +} + +void SetInjectableArgvs(const std::vector& new_argvs) { + SetInjectableArgvs( + new std::vector(new_argvs.begin(), new_argvs.end())); +} + +void ClearInjectableArgvs() { + delete g_injected_test_argvs; + g_injected_test_argvs = nullptr; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, int32_t* value) { + // Parses the environment variable as a decimal integer. + char* end = nullptr; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an int32_t? + const auto result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an int32_t. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true if and only if it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { +#if defined(GTEST_GET_BOOL_FROM_ENV_) + return GTEST_GET_BOOL_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == nullptr ? default_value + : strcmp(string_value, "0") != 0; +#endif // defined(GTEST_GET_BOOL_FROM_ENV_) +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +int32_t Int32FromGTestEnv(const char* flag, int32_t default_value) { +#if defined(GTEST_GET_INT32_FROM_ENV_) + return GTEST_GET_INT32_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == nullptr) { + // The environment variable is not set. + return default_value; + } + + int32_t result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +#endif // defined(GTEST_GET_INT32_FROM_ENV_) +} + +// As a special case for the 'output' flag, if GTEST_OUTPUT is not +// set, we look for XML_OUTPUT_FILE, which is set by the Bazel build +// system. The value of XML_OUTPUT_FILE is a filename without the +// "xml:" prefix of GTEST_OUTPUT. +// Note that this is meant to be called at the call site so it does +// not check that the flag is 'output' +// In essence this checks an env variable called XML_OUTPUT_FILE +// and if it is set we prepend "xml:" to its value, if it not set we return "" +std::string OutputFlagAlsoCheckEnvVar(){ + std::string default_value_for_output_flag = ""; + const char* xml_output_file_env = posix::GetEnv("XML_OUTPUT_FILE"); + if (nullptr != xml_output_file_env) { + default_value_for_output_flag = std::string("xml:") + xml_output_file_env; + } + return default_value_for_output_flag; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { +#if defined(GTEST_GET_STRING_FROM_ENV_) + return GTEST_GET_STRING_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == nullptr ? default_value : value; +#endif // defined(GTEST_GET_STRING_FROM_ENV_) +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + + +#include + +#include +#include +#include +#include // NOLINT +#include +#include + + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +// Helpers for widening a character to char32_t. Since the standard does not +// specify if char / wchar_t is signed or unsigned, it is important to first +// convert it to the unsigned type of the same width before widening it to +// char32_t. +template +char32_t ToChar32(CharType in) { + return static_cast( + static_cast::type>(in)); +} + +} // namespace + +namespace internal { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexadecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(char32_t c) { return 0x20 <= c && c <= 0x7E; } + +// Prints c (of type char, char8_t, char16_t, char32_t, or wchar_t) as a +// character literal without the quotes, escaping it when necessary; returns how +// c was formatted. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + const char32_t u_c = ToChar32(c); + switch (u_c) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(u_c)) { + *os << static_cast(c); + return kAsIs; + } else { + ostream::fmtflags flags = os->flags(); + *os << "\\x" << std::hex << std::uppercase << static_cast(u_c); + os->flags(flags); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a char32_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char32_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +static const char* GetCharWidthPrefix(char) { + return ""; +} + +static const char* GetCharWidthPrefix(signed char) { + return ""; +} + +static const char* GetCharWidthPrefix(unsigned char) { + return ""; +} + +#ifdef __cpp_char8_t +static const char* GetCharWidthPrefix(char8_t) { + return "u8"; +} +#endif + +static const char* GetCharWidthPrefix(char16_t) { + return "u"; +} + +static const char* GetCharWidthPrefix(char32_t) { + return "U"; +} + +static const char* GetCharWidthPrefix(wchar_t) { + return "L"; +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} + +#ifdef __cpp_char8_t +static CharFormat PrintAsStringLiteralTo(char8_t c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} +#endif + +static CharFormat PrintAsStringLiteralTo(char16_t c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} + +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} + +// Prints a character c (of type char, char8_t, char16_t, char32_t, or wchar_t) +// and its code. '\0' is printed as "'\\0'", other unprintable characters are +// also properly escaped using the standard C++ escape sequence. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << GetCharWidthPrefix(c) << "'"; + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexadecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); } +void PrintTo(signed char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); } + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); } + +// TODO(dcheng): Consider making this delegate to PrintCharAndCodeTo() as well. +void PrintTo(char32_t c, ::std::ostream* os) { + *os << std::hex << "U+" << std::uppercase << std::setfill('0') << std::setw(4) + << static_cast(c); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char, char8_t, char16_t, char32_t, or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static CharFormat PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const quote_prefix = GetCharWidthPrefix(*begin); + *os << quote_prefix << "\""; + bool is_previous_hex = false; + CharFormat print_format = kAsIs; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << quote_prefix << "\""; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + // Remember if any characters required hex escaping. + if (is_previous_hex) { + print_format = kHexEscape; + } + } + *os << "\""; + return print_format; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +#ifdef __cpp_char8_t +// Prints a (const) char8_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const char8_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} +#endif + +// Prints a (const) char16_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const char16_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) char32_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const char32_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +namespace { + +// Prints a null-terminated C-style string to the ostream. +template +void PrintCStringTo(const Char* s, ostream* os) { + if (s == nullptr) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, std::char_traits::length(s), os); + } +} + +} // anonymous namespace + +void PrintTo(const char* s, ostream* os) { PrintCStringTo(s, os); } + +#ifdef __cpp_char8_t +void PrintTo(const char8_t* s, ostream* os) { PrintCStringTo(s, os); } +#endif + +void PrintTo(const char16_t* s, ostream* os) { PrintCStringTo(s, os); } + +void PrintTo(const char32_t* s, ostream* os) { PrintCStringTo(s, os); } + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { PrintCStringTo(s, os); } +#endif // wchar_t is native + +namespace { + +bool ContainsUnprintableControlCodes(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast(str); + + for (size_t i = 0; i < length; i++) { + unsigned char ch = *s++; + if (std::iscntrl(ch)) { + switch (ch) { + case '\t': + case '\n': + case '\r': + break; + default: + return true; + } + } + } + return false; +} + +bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t<= 0xbf; } + +bool IsValidUTF8(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast(str); + + for (size_t i = 0; i < length;) { + unsigned char lead = s[i++]; + + if (lead <= 0x7f) { + continue; // single-byte character (ASCII) 0..7F + } + if (lead < 0xc2) { + return false; // trail byte or non-shortest form + } else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) { + ++i; // 2-byte character + } else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + // check for non-shortest form and surrogate + (lead != 0xe0 || s[i] >= 0xa0) && + (lead != 0xed || s[i] < 0xa0)) { + i += 2; // 3-byte character + } else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + IsUTF8TrailByte(s[i + 2]) && + // check for non-shortest form + (lead != 0xf0 || s[i] >= 0x90) && + (lead != 0xf4 || s[i] < 0x90)) { + i += 3; // 4-byte character + } else { + return false; + } + } + return true; +} + +void ConditionalPrintAsText(const char* str, size_t length, ostream* os) { + if (!ContainsUnprintableControlCodes(str, length) && + IsValidUTF8(str, length)) { + *os << "\n As Text: \"" << str << "\""; + } +} + +} // anonymous namespace + +void PrintStringTo(const ::std::string& s, ostream* os) { + if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) { + if (GTEST_FLAG(print_utf8)) { + ConditionalPrintAsText(s.data(), s.size(), os); + } + } +} + +#ifdef __cpp_char8_t +void PrintU8StringTo(const ::std::u8string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif + +void PrintU16StringTo(const ::std::u16string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +void PrintU32StringTo(const ::std::u32string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) + + + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == nullptr ? message : std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os << internal::FormatFileLocation(result.file_name(), + result.line_number()) + << " " + << (result.type() == TestPartResult::kSuccess + ? "Success" + : result.type() == TestPartResult::kSkip + ? "Skipped" + : result.type() == TestPartResult::kFatalFailure + ? "Fatal failure" + : "Non-fatal failure") + << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[static_cast(index)]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +namespace testing { +namespace internal { + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +static std::vector SplitIntoTestNames(const char* src) { + std::vector name_vec; + src = SkipSpaces(src); + for (; src != nullptr; src = SkipComma(src)) { + name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src))); + } + return name_vec; +} + +// Verifies that registered_tests match the test names in +// registered_tests_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestSuitePState::VerifyRegisteredTestNames( + const char* test_suite_name, const char* file, int line, + const char* registered_tests) { + RegisterTypeParameterizedTestSuite(test_suite_name, CodeLocation(file, line)); + + typedef RegisteredTestsMap::const_iterator RegisteredTestIter; + registered_ = true; + + std::vector name_vec = SplitIntoTestNames(registered_tests); + + Message errors; + + std::set tests; + for (std::vector::const_iterator name_it = name_vec.begin(); + name_it != name_vec.end(); ++name_it) { + const std::string& name = *name_it; + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + if (registered_tests_.count(name) != 0) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test suite.\n"; + } + } + + for (RegisteredTestIter it = registered_tests_.begin(); + it != registered_tests_.end(); + ++it) { + if (tests.count(it->first) == 0) { + errors << "You forgot to list test " << it->first << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +} // namespace internal +} // namespace testing diff --git a/test/gtest/gtest.h b/test/gtest/gtest.h new file mode 100644 index 0000000000000000000000000000000000000000..e7490573ac5fbf630111d2ac9d63ad1906f6e2a9 --- /dev/null +++ b/test/gtest/gtest.h @@ -0,0 +1,12377 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include +#include +#include +#include + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_DRAGONFLY - DragonFlyBSD +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD +// GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_OS2 - OS/2 +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #if +// which controls that code. For example: +// +// #if GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GOOGLETEST_CM0007 DO NOT DELETE +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables copy operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_DISALLOW_MOVE_ASSIGN_ - disables move operator=. +// GTEST_DISALLOW_MOVE_AND_ASSIGN_ - disables move ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter or +// UniversalPrinter specializations. +// GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter +// or +// UniversalPrinter +// specializations. +// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher or +// Matcher +// specializations. +// GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter or +// UniversalPrinter +// specializations. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like platforms +// GOOGLETEST_CM0008 DO NOT DELETE +// or a reduced regular exception syntax on other +// platforms, including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// TimeInMillis - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an int32_t environment variable. +// StringFromGTestEnv() - parses a string environment variable. +// +// Deprecation warnings: +// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as +// deprecated; calling a marked function +// should generate a compiler warning + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include + +#include +#include +#include +#include + +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT +#include +#include +#include // NOLINT +#include +#include // NOLINT + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +# elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +# define GTEST_OS_WINDOWS_MINGW 1 +# define GTEST_OS_WINDOWS 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GTEST_OS_WINDOWS_DESKTOP 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +# define GTEST_OS_WINDOWS_PHONE 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +# define GTEST_OS_WINDOWS_RT 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +# define GTEST_OS_WINDOWS_PHONE 1 +# define GTEST_OS_WINDOWS_TV_TITLE 1 +# else + // WINAPI_FAMILY defined but no known partition matched. + // Default to desktop. +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __OS2__ +# define GTEST_OS_OS2 1 +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# include +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# endif +#elif defined __DragonFly__ +# define GTEST_OS_DRAGONFLY 1 +#elif defined __FreeBSD__ +# define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +# define GTEST_OS_FUCHSIA 1 +#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__) +# define GTEST_OS_GNU_KFREEBSD 1 +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +# define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#elif defined(__HAIKU__) +#define GTEST_OS_HAIKU 1 +#elif defined ESP8266 +#define GTEST_OS_ESP8266 1 +#elif defined ESP32 +#define GTEST_OS_ESP32 1 +#elif defined(__XTENSA__) +#define GTEST_OS_XTENSA 1 +#endif // __CYGWIN__ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +#if !defined(GTEST_DEV_EMAIL_) +# define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +# define GTEST_FLAG_PREFIX_ "gtest_" +# define GTEST_FLAG_PREFIX_DASH_ "gtest-" +# define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +# define GTEST_NAME_ "Google Test" +# define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +# define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if defined(_MSC_VER) +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) \ + __pragma(warning(disable: warnings)) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() \ + __pragma(warning(pop)) +#else +// Not all compilers are MSVC +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + _Pragma("clang diagnostic pop") +#else +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS_MOBILE +# include +# include +# endif +// In order to avoid having to include , use forward declaration +#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#elif GTEST_OS_XTENSA +#include +// Xtensa toolchains define strcasecmp in the string.h header instead of +// strings.h. string.h is already included. +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include +# include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true if and only if Google Test can use POSIX regular +// expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +#define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS && !GTEST_OS_XTENSA) +# endif +#endif + +#if GTEST_USES_PCRE +// The appropriate headers have already been included. + +#elif GTEST_HAS_POSIX_RE + +// On some platforms, needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included , which is guaranteed to define size_t through +// . +# include // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_USES_PCRE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__clang__) +// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang +// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files, +// there can be cleanups for ObjC exceptions which also need cleanups, even if +// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which +// checks for C++ exceptions starting at clang r206352, but which checked for +// cleanups prior to that. To reliably check for C++ exception availability with +// clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +#define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || GTEST_OS_XTENSA)) + +#endif // GTEST_HAS_STD_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is +// enabled. +# elif defined(__GNUC__) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#define GTEST_HAS_PTHREAD \ + (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD || \ + GTEST_OS_HAIKU) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +# include // NOLINT + +// For timespec and nanosleep, used below. +# include // NOLINT +#endif + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +# if defined(__LP64__) || \ + (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW || \ + GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU) +# define GTEST_HAS_DEATH_TEST 1 +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#elif defined(__clang__) +# if __has_attribute(unused) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +# endif +#endif +#ifndef GTEST_ATTRIBUTE_UNUSED_ +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// Use this annotation before a function that takes a printf format string. +#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC) +# if defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \ + first_to_check))) +# else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +# endif +#else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + + +// A macro to disallow copy operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type) \ + type& operator=(type const &) = delete + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \ + type(type const&) = delete; \ + type& operator=(type const&) = delete + +// A macro to disallow move operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_MOVE_ASSIGN_(type) \ + type& operator=(type &&) noexcept = delete + +// A macro to disallow move constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_MOVE_AND_ASSIGN_(type) \ + type(type&&) noexcept = delete; \ + type& operator=(type&&) noexcept = delete + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && !COMPILER_ICC + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +# define GTEST_INTENTIONAL_CONST_COND_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifndef GTEST_IS_THREADSAFE + +#define GTEST_IS_THREADSAFE \ + (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \ + GTEST_HAS_PTHREAD) + +#endif // GTEST_IS_THREADSAFE + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif +#elif __GNUC__ >= 4 || defined(__clang__) +# define GTEST_API_ __attribute__((visibility ("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +# define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +# define GTEST_HAS_CXXABI_H_ 1 +# else +# define GTEST_HAS_CXXABI_H_ 0 +# endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if defined(__clang__) +# if __has_feature(memory_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \ + __attribute__((no_sanitize_memory)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +# endif // __has_feature(memory_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif // __clang__ + +// A function level attribute to disable AddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(address_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +# endif // __has_feature(address_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif // __clang__ + +// A function level attribute to disable HWAddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(hwaddress_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ + __attribute__((no_sanitize("hwaddress"))) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +# endif // __has_feature(hwaddress_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +#endif // __clang__ + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(thread_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \ + __attribute__((no_sanitize_thread)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +# endif // __has_feature(thread_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif // __clang__ + +namespace testing { + +class Message; + +// Legacy imports for backwards compatibility. +// New code should use std:: names directly. +using std::get; +using std::make_tuple; +using std::tuple; +using std::tuple_element; +using std::tuple_size; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ is a legacy macro used to verify that a compile +// time expression is true (in new code, use static_assert instead). For +// example, you could use it to verify the size of a static array: +// +// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES, +// names_incorrect_size); +// +// The second argument to the macro must be a valid C++ identifier. If the +// expression is false, compiler will issue an error containing this identifier. +#define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg) + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines RE. + +#if GTEST_USES_PCRE +// if used, PCRE is injected by custom/gtest-port.h +#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true if and only if regular expression re + // matches the entire str. + // PartialMatch(str, re) returns true if and only if regular expression re + // matches a substring of str (including str itself). + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + const char* pattern_; + bool is_valid_; + +# if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +# else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +# endif +}; + +#endif // GTEST_USES_PCRE + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#if !defined(GTEST_LOG_) + +# define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(nullptr); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +# define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { typedef const T& type; }; +template +struct ConstRef { typedef T& type; }; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (false) { + GTEST_INTENTIONAL_CONST_COND_POP_() + const To to = nullptr; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == nullptr || dynamic_cast(f) != nullptr); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); +#endif + +#if GTEST_HAS_DOWNCAST_ + return ::down_cast(base); +#elif GTEST_HAS_RTTI + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector GetArgvs(); + +#if GTEST_HAS_DEATH_TEST + +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#if GTEST_IS_THREADSAFE +# if GTEST_HAS_PTHREAD +// Sleeps for (roughly) n milliseconds. This function is only for testing +// Google Test's own constructs. Don't use it in user tests, either +// directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, nullptr); +} +# endif // GTEST_HAS_PTHREAD + +# if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_HAS_PTHREAD +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +GTEST_API_ void SleepMilliseconds(int n); + +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true if and only if the handle is a valid handle object that can be + // closed. + bool IsCloseable() const; + + Handle handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class GTEST_API_ Notification { + public: + Notification(); + void Notify(); + void WaitForNotification(); + + private: + AutoHandle event_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; +# endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return nullptr; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() override { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr)); + finished_ = true; + } + } + + void Run() override { + if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true if and only if we know that the thread function has + // finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; +# endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +# if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase); +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) { + } + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) + : func_(func), + param_(param) { + } + virtual ~RunnableImpl() {} + virtual void Run() { + func_(param_); + } + + private: + UserThreadFunc* const func_; + const T param_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl); + }; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer(); + } + + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + std::unique_ptr default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != nullptr) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + std::unique_ptr default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +#ifdef __cpp_char8_t +inline bool IsXDigit(char8_t ch) { + return isxdigit(static_cast(ch)) != 0; +} +#endif +inline bool IsXDigit(char16_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(char32_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) + it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int DoIsATTY(int /* fd */) { return 0; } +# else +inline int DoIsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#elif GTEST_OS_ESP8266 +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +inline int IsATTY(int fd) { + // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout + // to a file on Linux), which is unexpected, so save the previous value, and + // restore it after the call. + int savedErrno = errno; + int isAttyValue = DoIsATTY(fd); + errno = savedErrno; + + return isAttyValue; +} + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ + !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + struct wchar_codecvt : public std::codecvt {}; + std::wstring_convert converter; + std::wstring wide_path = converter.from_bytes(path); + std::wstring wide_mode = converter.from_bytes(mode); + return _wfopen(wide_path.c_str(), wide_mode.c_str()); +#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + return fopen(path, mode); +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA + // We are on an embedded platform, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. + return nullptr; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != nullptr && env[0] != '\0') ? env : nullptr; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +[[noreturn]] void Abort(); +#else +[[noreturn]] inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The biggest signed integer type the compiler supports. +// +// long long is guaranteed to be at least 64-bits in C++11. +using BiggestInt = long long; // NOLINT + +// The maximum number a BiggestInt can represent. +constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits::max)(); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + using UInt = void; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + using Int = std::int32_t; + using UInt = std::uint32_t; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + using Int = std::int64_t; + using UInt = std::uint64_t; +}; + +// Integer types of known sizes. +using TimeInMillis = int64_t; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +# define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +#if !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) +# define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 +#endif // !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) + +#if !defined(GTEST_DECLARE_bool_) +# define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +// Macros for declaring flags. +# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +# define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern std::int32_t GTEST_FLAG(name) +# define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +# define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +#endif // !defined(GTEST_DECLARE_bool_) + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +# define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +# define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +GTEST_API_ bool ParseInt32(const Message& src_text, const char* str, + int32_t* value); + +// Parses a bool/int32_t/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#if !defined(GTEST_INTERNAL_DEPRECATED) + +// Internal Macro to mark an API deprecated, for googletest usage only +// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or +// GTEST_INTERNAL_DEPRECATED(message) myFunction(); Every usage of +// a deprecated entity will trigger a warning when compiled with +// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). +// For msvc /W3 option will need to be used +// Note that for 'other' compilers this macro evaluates to nothing to prevent +// compilations errors. +#if defined(_MSC_VER) +#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__GNUC__) +#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define GTEST_INTERNAL_DEPRECATED(message) +#endif + +#endif // !defined(GTEST_INTERNAL_DEPRECATED) + +#if GTEST_HAS_ABSL +// Always use absl::any for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_ANY 1 +#include "absl/types/any.h" +namespace testing { +namespace internal { +using Any = ::absl::any; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::any for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_ANY 1 +#include +namespace testing { +namespace internal { +using Any = ::std::any; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::any is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::optional for UniversalPrinter<> specializations if +// googletest is built with absl support. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include "absl/types/optional.h" +namespace testing { +namespace internal { +template +using Optional = ::absl::optional; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::optional for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include +namespace testing { +namespace internal { +template +using Optional = ::std::optional; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::optional is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::string_view for Matcher<> specializations if googletest +// is built with absl support. +# define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include "absl/strings/string_view.h" +namespace testing { +namespace internal { +using StringView = ::absl::string_view; +} // namespace internal +} // namespace testing +#else +# ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::string_view for Matcher<> +// specializations. +# define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include +namespace testing { +namespace internal { +using StringView = ::std::string_view; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::string_view is not +// supported. +# endif // __has_include() && __cplusplus >= 201703L +# endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::variant for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include "absl/types/variant.h" +namespace testing { +namespace internal { +template +using Variant = ::absl::variant; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::variant for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include +namespace testing { +namespace internal { +template +using Variant = ::std::variant; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::variant is not supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#if GTEST_OS_LINUX +# include +# include +# include +# include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include +#include +#include + + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == nullptr) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + // We'll hold the text streamed to this object here. + const std::unique_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by gtest-internal.h. +// It should not be #included by other files. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include +#include + + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true if and only if they have the same + // content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true if and only if they have the + // same content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true if and only if the given string ends with the given suffix, + // ignoring case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value to given width with leading zeros. + static std::string FormatIntWidthN(int value, int width); + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats an int value as "%X". + static std::string FormatHexUInt32(uint32_t value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true if and only if the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurrence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type utilities needed for implementing typed and type-parameterized +// tests. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + +#if GTEST_HAS_RTTI +// GetTypeName(const std::type_info&) returns a human-readable name of type T. +inline std::string GetTypeName(const std::type_info& type) { + const char* const name = type.name(); +#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +#if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +#endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +#else + return name; +#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC +} +#endif // GTEST_HAS_RTTI + +// GetTypeName() returns a human-readable name of type T if and only if +// RTTI is enabled, otherwise it returns a dummy type name. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +#if GTEST_HAS_RTTI + return GetTypeName(typeid(T)); +#else + return ""; +#endif // GTEST_HAS_RTTI +} + +// A unique type indicating an empty node +struct None {}; + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +template +struct Templates { + using Head = TemplateSel; + using Tail = Templates; +}; + +template +struct Templates { + using Head = TemplateSel; + using Tail = None; +}; + +// Tuple-like type lists +template +struct Types { + using Head = Head_; + using Tail = Types; +}; + +template +struct Types { + using Head = Head_; + using Tail = None; +}; + +// Helper metafunctions to tell apart a single type from types +// generated by ::testing::Types +template +struct ProxyTypeList { + using type = Types; +}; + +template +struct is_proxy_type_list : std::false_type {}; + +template +struct is_proxy_type_list> : std::true_type {}; + +// Generator which conditionally creates type lists. +// It recognizes if a requested type list should be created +// and prevents creating a new type list nested within another one. +template +struct GenerateTypeList { + private: + using proxy = typename std::conditional::value, T, + ProxyTypeList>::type; + + public: + using type = typename proxy::type; +}; + +} // namespace internal + +template +using Types = internal::ProxyTypeList; + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +// Stringifies its argument. +// Work around a bug in visual studio which doesn't accept code like this: +// +// #define GTEST_STRINGIFY_(name) #name +// #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ... +// MACRO(, x, y) +// +// Complaining about the argument to GTEST_STRINGIFY_ being empty. +// This is allowed by the spec. +#define GTEST_STRINGIFY_HELPER_(name, ...) #name +#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, ) + +namespace proto2 { +class MessageLite; +} + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test suites. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// An IgnoredValue object can be implicitly constructed from ANY value. +class IgnoredValue { + struct Sink {}; + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + // Disable the conversion if T already has a magical conversion operator. + // Otherwise we get ambiguity. + template ::value, + int>::type = 0> + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \ +/* an exported class was derived from a class that was not exported */) + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); + +} // namespace edit_distance + +// Calculate the diff between 'left' and 'right' and return it in unified diff +// format. +// If not null, stores in 'total_line_count' the total number of lines found +// in left + right. +GTEST_API_ std::string DiffStrings(const std::string& left, + const std::string& right, + size_t* total_line_count); + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const uint32_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true if and only if this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true if and only if this number is at most kMaxUlps ULP's away + // from rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test suite, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + Test* CreateTest() override { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestSuite() and TearDownTestSuite() functions. +using SetUpTestSuiteFunc = void (*)(); +using TearDownTestSuiteFunc = void (*)(); + +struct CodeLocation { + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} + + std::string file; + int line; +}; + +// Helper to identify which setup function for TestCase / TestSuite to call. +// Only one function is allowed, either TestCase or TestSute but not both. + +// Utility functions to help SuiteApiResolver +using SetUpTearDownSuiteFuncType = void (*)(); + +inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull( + SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) { + return a == def ? nullptr : a; +} + +template +// Note that SuiteApiResolver inherits from T because +// SetUpTestSuite()/TearDownTestSuite() could be protected. Ths way +// SuiteApiResolver can access them. +struct SuiteApiResolver : T { + // testing::Test is only forward declared at this point. So we make it a + // dependend class for the compiler to be OK with it. + using Test = + typename std::conditional::type; + + static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both SetUpTestSuite and SetUpTestCase, please " + "make sure there is only one present at " + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::SetUpTestSuite; +#endif + } + + static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both TearDownTestSuite and TearDownTestCase," + " please make sure there is only one present at" + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::TearDownTestSuite; +#endif + } +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// State of the definition of a type-parameterized test suite. +class GTEST_API_ TypedTestSuitePState { + public: + TypedTestSuitePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test suite hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, + "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.insert( + ::std::make_pair(test_name, CodeLocation(file, line))); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames(const char* test_suite_name, + const char* file, int line, + const char* registered_tests); + + private: + typedef ::std::map RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TypedTestCasePState = TypedTestSuitePState; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == nullptr) { + return nullptr; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == nullptr ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest); + +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template +struct NameGeneratorSelector { + typedef Provided type; +}; + +template +void GenerateNamesRecursively(internal::None, std::vector*, int) {} + +template +void GenerateNamesRecursively(Types, std::vector* result, int i) { + result->push_back(NameGenerator::template GetName(i)); + GenerateNamesRecursively(typename Types::Tail(), result, + i + 1); +} + +template +std::vector GenerateNames() { + std::vector result; + GenerateNamesRecursively(Types(), &result, 0); + return result; +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const CodeLocation& code_location, + const char* case_name, const char* test_names, int index, + const std::vector& type_names = + GenerateNames()) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[static_cast(index)]) + .c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName().c_str(), + nullptr, // No value parameter. + code_location, GetTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite( + code_location.file.c_str(), code_location.line), + SuiteApiResolver::GetTearDownCaseOrSuite( + code_location.file.c_str(), code_location.line), + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest::Register(prefix, + code_location, + case_name, + test_names, + index + 1, + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location); +GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation( + const char* case_name); + +// TypeParameterizedTestSuite::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestSuitePState* state, const char* case_name, + const char* test_names, + const std::vector& type_names = + GenerateNames()) { + RegisterTypeParameterizedTestSuiteInstantiation(case_name); + std::string test_name = StripTrailingSpaces( + GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), + code_location.line).c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, test_location, case_name, test_names, 0, type_names); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestSuite::Register(prefix, code_location, + state, case_name, + SkipComma(test_names), + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestSuitePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// Helper for declaring std::string within 'if' statement +// in pre C++17 build environment. +struct TrueWithString { + TrueWithString() = default; + explicit TrueWithString(const char* str) : value(str) {} + explicit TrueWithString(const std::string& str) : value(str) {} + explicit operator bool() const { return true; } + std::string value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const uint32_t kMaxRange = 1u << 31; + + explicit Random(uint32_t seed) : state_(seed) {} + + void Reseed(uint32_t seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + uint32_t Generate(uint32_t range); + + private: + uint32_t state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + typename std::remove_const::type>::type + +// HasDebugStringAndShortDebugString::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. +template +class HasDebugStringAndShortDebugString { + private: + template + static auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().DebugString())>::type; + template + static std::false_type CheckDebugString(...); + + template + static auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().ShortDebugString())>::type; + template + static std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +template +constexpr bool HasDebugStringAndShortDebugString::value; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(nullptr, nullptr)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template (0)) == sizeof(IsContainer)> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public std::false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl { + using value_type = decltype(*std::declval()); + using type = + std::is_same::type>::type, + C>; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + static_assert(!std::is_const::value, "Type must not be const"); + static_assert(!std::is_reference::value, + "Type must not be a reference"); + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); +}; + +// Backport of std::index_sequence. +template +struct IndexSequence { + using type = IndexSequence; +}; + +// Double the IndexSequence, and one if plus_one is true. +template +struct DoubleSequence; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; + +// Backport of std::make_index_sequence. +// It uses O(ln(N)) instantiation depth. +template +struct MakeIndexSequenceImpl + : DoubleSequence::type, + N / 2>::type {}; + +template <> +struct MakeIndexSequenceImpl<0> : IndexSequence<> {}; + +template +using MakeIndexSequence = typename MakeIndexSequenceImpl::type; + +template +using IndexSequenceFor = typename MakeIndexSequence::type; + +template +struct Ignore { + Ignore(...); // NOLINT +}; + +template +struct ElemFromListImpl; +template +struct ElemFromListImpl> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; + +template +struct ElemFromList { + using type = + decltype(ElemFromListImpl::type>::Apply( + static_cast(nullptr)...)); +}; + +struct FlatTupleConstructTag {}; + +template +class FlatTuple; + +template +struct FlatTupleElemBase; + +template +struct FlatTupleElemBase, I> { + using value_type = typename ElemFromList::type; + FlatTupleElemBase() = default; + template + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward(t)) {} + value_type value; +}; + +template +struct FlatTupleBase; + +template +struct FlatTupleBase, IndexSequence> + : FlatTupleElemBase, Idx>... { + using Indices = IndexSequence; + FlatTupleBase() = default; + template + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase, Idx>(FlatTupleConstructTag{}, + std::forward(args))... {} + + template + const typename ElemFromList::type& Get() const { + return FlatTupleElemBase, I>::value; + } + + template + typename ElemFromList::type& Get() { + return FlatTupleElemBase, I>::value; + } + + template + auto Apply(F&& f) -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } + + template + auto Apply(F&& f) const -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } +}; + +// Analog to std::tuple but with different tradeoffs. +// This class minimizes the template instantiation depth, thus allowing more +// elements than std::tuple would. std::tuple has been seen to require an +// instantiation depth of more than 10x the number of elements in some +// implementations. +// FlatTuple and ElemFromList are not recursive and have a fixed depth +// regardless of T... +// MakeIndexSequence, on the other hand, it is recursive but with an +// instantiation depth of O(ln(N)). +template +class FlatTuple + : private FlatTupleBase, + typename MakeIndexSequence::type> { + using Indices = typename FlatTupleBase< + FlatTuple, typename MakeIndexSequence::type>::Indices; + + public: + FlatTuple() = default; + template + explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args) + : FlatTuple::FlatTupleBase(tag, std::forward(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; +}; + +// Utility functions to be called with static_assert to induce deprecation +// warnings. +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TEST_SUITE_P") +constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE_P is deprecated, please use " + "TYPED_TEST_SUITE_P") +constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE is deprecated, please use " + "TYPED_TEST_SUITE") +constexpr bool TypedTestCaseIsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " + "REGISTER_TYPED_TEST_SUITE_P") +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TYPED_TEST_SUITE_P") +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + +} // namespace internal +} // namespace testing + +namespace std { +// Some standard library implementations use `struct tuple_size` and some use +// `class tuple_size`. Clang warns about the mismatch. +// https://reviews.llvm.org/D55466 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +struct tuple_size> + : std::integral_constant {}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace std + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +#define GTEST_SKIP_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) + +// Suppress MSVC warning 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +// NOTE: The "else" is important to keep this expansion to prevent a top-level +// "else" from attaching to our "if". +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } else /* NOLINT */ \ + static_assert(true, "") // User must have a semicolon after expansion. + +#if GTEST_HAS_EXCEPTIONS + +namespace testing { +namespace internal { + +class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } +}; + +} // namespace internal +} // namespace testing + +#if GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e)) + +#else // GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) \ + std::string { "an std::exception-derived error" } + +#endif // GTEST_HAS_RTTI + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (typename std::conditional< \ + std::is_same::type>::type, \ + std::exception>::value, \ + const ::testing::internal::NeverThrown&, const std::exception&>::type \ + e) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#if GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (std::exception const& e) { \ + gtest_msg.value = "it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (...) { \ + gtest_msg.value = "it throws."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail(("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: " + gtest_msg.value).c_str()) + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// representation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + \ + private: \ + void TestBody() override; \ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include +#include +#include +#include +#include + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include + + +namespace testing { + +// Definitions in the internal* namespaces are subject to change without notice. +// DO NOT USE THEM IN USER CODE! +namespace internal { + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +struct ContainerPrinter { + template (0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value>::type> + static void PrintValue(const T& container, std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (auto&& elem : container) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(elem, os) here as PrintTo() doesn't + // handle `elem` being a native array. + internal::UniversalPrint(elem, os); + ++count; + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; + } +}; + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +struct FunctionPointerPrinter { + template ::value>::type> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); + } + } +}; + +struct PointerPrinter { + template + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } + } +}; + +namespace internal_stream_operator_without_lexical_name_lookup { + +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); + +struct StreamPrinter { + template ::value>::type, + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + typename = decltype(std::declval() + << std::declval())> + static void PrintValue(const T& value, ::std::ostream* os) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. + *os << value; + } +}; + +} // namespace internal_stream_operator_without_lexical_name_lookup + +struct ProtobufPrinter { + // We print a protobuf using its ShortDebugString() when the string + // doesn't exceed this many characters; otherwise we print it using + // DebugString() for better readability. + static const size_t kProtobufOneLinerMaxLength = 50; + + template ::value>::type> + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +struct ConvertibleToIntegerPrinter { + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(internal::BiggestInt value, ::std::ostream* os) { + *os << value; + } +}; + +struct ConvertibleToStringViewPrinter { +#if GTEST_INTERNAL_HAS_STRING_VIEW + static void PrintValue(internal::StringView value, ::std::ostream* os) { + internal::UniversalPrint(value, os); + } +#endif +}; + + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); +struct RawBytesPrinter { + // SFINAE on `sizeof` to make sure we have a complete type. + template + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo( + static_cast( + // Load bearing cast to void* to support iOS + reinterpret_cast(std::addressof(value))), + sizeof(value), os); + } +}; + +struct FallbackPrinter { + template + static void PrintValue(const T&, ::std::ostream* os) { + *os << "(incomplete type)"; + } +}; + +// Try every printer in order and return the first one that works. +template +struct FindFirstPrinter : FindFirstPrinter {}; + +template +struct FindFirstPrinter< + T, decltype(Printer::PrintValue(std::declval(), nullptr)), + Printer, Printers...> { + using type = Printer; +}; + +// Select the best printer in the following order: +// - Print containers (they have begin/end/etc). +// - Print function pointers. +// - Print object pointers. +// - Use the stream operator, if available. +// - Print protocol buffers. +// - Print types convertible to BiggestInt. +// - Print types convertible to StringView, if available. +// - Fallback to printing the raw bytes of the object. +template +void PrintWithFallback(const T& value, ::std::ostream* os) { + using Printer = typename FindFirstPrinter< + T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ProtobufPrinter, ConvertibleToIntegerPrinter, + ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type; + Printer::PrintValue(value, os); +} + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); +#ifdef __cpp_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); +#ifdef __cpp_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string); + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + internal::PrintWithFallback(value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); +inline void PrintTo(char16_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#ifdef __cpp_char8_t +inline void PrintTo(char8_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#endif + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#ifdef __cpp_char8_t +// Overloads for u8 strings. +GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); +inline void PrintTo(char8_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif +// Overloads for u16 strings. +GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os); +inline void PrintTo(char16_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +// Overloads for u32 strings. +GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os); +inline void PrintTo(char32_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::std::string. +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::std::u8string +#ifdef __cpp_char8_t +GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { + PrintU8StringTo(s, os); +} +#endif + +// Overloads for ::std::u16string +GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) { + PrintU16StringTo(s, os); +} + +// Overloads for ::std::u32string +GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) { + PrintU32StringTo(s, os); +} + +// Overloads for ::std::wstring. +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Overload for internal::StringView. +inline void PrintTo(internal::StringView sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } + +template +void PrintTo(std::reference_wrapper ref, ::std::ostream* os) { + UniversalPrinter::Print(ref.get(), os); +} + +inline const void* VoidifyPointer(const void* p) { return p; } +inline const void* VoidifyPointer(volatile const void* p) { + return const_cast(p); +} + +template +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + // We can't print the value. Just print the pointer.. + *os << "(" << (VoidifyPointer)(ptr.get()) << ")"; + } +} +template ::value && + !std::is_array::value>::type> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = "; + UniversalPrinter::Print(*ptr, os); + *os << ")"; + } +} + +template +void PrintTo(const std::unique_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +template +void PrintTo(const std::shared_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T&, std::integral_constant, + ::std::ostream*) {} + +template +void PrintTupleTo(const T& t, std::integral_constant, + ::std::ostream* os) { + PrintTupleTo(t, std::integral_constant(), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (I > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter::type>::Print( + std::get(t), os); +} + +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + *os << "("; + PrintTupleTo(t, std::integral_constant(), os); + *os << ")"; +} + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Remove any const-qualifiers before passing a type to UniversalPrinter. +template +class UniversalPrinter : public UniversalPrinter {}; + +#if GTEST_INTERNAL_HAS_ANY + +// Printer for std::any / absl::any + +template <> +class UniversalPrinter { + public: + static void Print(const Any& value, ::std::ostream* os) { + if (value.has_value()) { + *os << "value of type " << GetTypeName(value); + } else { + *os << "no value"; + } + } + + private: + static std::string GetTypeName(const Any& value) { +#if GTEST_HAS_RTTI + return internal::GetTypeName(value.type()); +#else + static_cast(value); // possibly unused + return ""; +#endif // GTEST_HAS_RTTI + } +}; + +#endif // GTEST_INTERNAL_HAS_ANY + +#if GTEST_INTERNAL_HAS_OPTIONAL + +// Printer for std::optional / absl::optional + +template +class UniversalPrinter> { + public: + static void Print(const Optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +#endif // GTEST_INTERNAL_HAS_OPTIONAL + +#if GTEST_INTERNAL_HAS_VARIANT + +// Printer for std::variant / absl::variant + +template +class UniversalPrinter> { + public: + static void Print(const Variant& value, ::std::ostream* os) { + *os << '('; +#if GTEST_HAS_ABSL + absl::visit(Visitor{os, value.index()}, value); +#else + std::visit(Visitor{os, value.index()}, value); +#endif // GTEST_HAS_ABSL + *os << ')'; + } + + private: + struct Visitor { + template + void operator()(const U& u) const { + *os << "'" << GetTypeName() << "(index = " << index + << ")' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + std::size_t index; + }; +}; + +#endif // GTEST_INTERNAL_HAS_VARIANT + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +#ifdef __cpp_char8_t +// This overload prints a (const) char8_t array compactly. +GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, + ::std::ostream* os); +#endif + +// This overload prints a (const) char16_t array compactly. +GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) char32_t array compactly. +GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter : public UniversalTersePrinter { +}; + +#ifdef __cpp_char8_t +template <> +class UniversalTersePrinter { + public: + static void Print(const char8_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u8string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(const char16_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u16string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +template <> +class UniversalTersePrinter { + public: + static void Print(const char32_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u32string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +typedef ::std::vector< ::std::string> Strings; + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. +template +void TersePrintPrefixToStrings(const Tuple&, std::integral_constant, + Strings*) {} +template +void TersePrintPrefixToStrings(const Tuple& t, + std::integral_constant, + Strings* strings) { + TersePrintPrefixToStrings(t, std::integral_constant(), + strings); + ::std::stringstream ss; + UniversalTersePrint(std::get(t), &ss); + strings->push_back(ss.str()); +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TersePrintPrefixToStrings( + value, std::integral_constant::value>(), + &result); + return result; +} + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GTEST_MAYBE_5046_ 5046 +#else +#define GTEST_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherMatcher that implements the matcher interface: +// using is_gtest_matcher = void; +// bool MatchAndExplain(const T&, std::ostream*); +// (MatchResultListener* can also be used instead of std::ostream*) +// void DescribeTo(std::ostream*); +// void DescribeNegationTo(std::ostream*); +// +// 2. a factory function that creates a Matcher object from a +// FooMatcherMatcher. + +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != nullptr) *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true if and only if the listener is interested in an explanation + // of the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != nullptr; } + + private: + ::std::ostream* const stream_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); +}; + +inline MatchResultListener::~MatchResultListener() { +} + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class GTEST_API_ MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() {} + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Test) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +namespace internal { + +struct AnyEq { + template + bool operator()(const A& a, const B& b) const { return a == b; } +}; +struct AnyNe { + template + bool operator()(const A& a, const B& b) const { return a != b; } +}; +struct AnyLt { + template + bool operator()(const A& a, const B& b) const { return a < b; } +}; +struct AnyGt { + template + bool operator()(const A& a, const B& b) const { return a > b; } +}; +struct AnyLe { + template + bool operator()(const A& a, const B& b) const { return a <= b; } +}; +struct AnyGe { + template + bool operator()(const A& a, const B& b) const { return a >= b; } +}; + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(nullptr) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); +}; + +struct SharedPayloadBase { + std::atomic ref{1}; + void Ref() { ref.fetch_add(1, std::memory_order_relaxed); } + bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; } +}; + +template +struct SharedPayload : SharedPayloadBase { + explicit SharedPayload(const T& v) : value(v) {} + explicit SharedPayload(T&& v) : value(std::move(v)) {} + + static void Destroy(SharedPayloadBase* shared) { + delete static_cast(shared); + } + + T value; +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase : private MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener'. + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + GTEST_CHECK_(vtable_ != nullptr); + return vtable_->match_and_explain(*this, x, listener); + } + + // Returns true if and only if this matcher matches x. + bool Matches(const T& x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, false); + } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, true); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(const T& x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + if (vtable_ == nullptr) return nullptr; + return vtable_->get_describer(*this); + } + + protected: + MatcherBase() : vtable_(nullptr) {} + + // Constructs a matcher from its implementation. + template + explicit MatcherBase(const MatcherInterface* impl) { + Init(impl); + } + + template ::type::is_gtest_matcher> + MatcherBase(M&& m) { // NOLINT + Init(std::forward(m)); + } + + MatcherBase(const MatcherBase& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + if (IsShared()) buffer_.shared->Ref(); + } + + MatcherBase& operator=(const MatcherBase& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + if (IsShared()) buffer_.shared->Ref(); + return *this; + } + + MatcherBase(MatcherBase&& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + other.vtable_ = nullptr; + } + + MatcherBase& operator=(MatcherBase&& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + other.vtable_ = nullptr; + return *this; + } + + ~MatcherBase() override { Destroy(); } + + private: + struct VTable { + bool (*match_and_explain)(const MatcherBase&, const T&, + MatchResultListener*); + void (*describe)(const MatcherBase&, std::ostream*, bool negation); + // Returns the captured object if it implements the interface, otherwise + // returns the MatcherBase itself. + const MatcherDescriberInterface* (*get_describer)(const MatcherBase&); + // Called on shared instances when the reference count reaches 0. + void (*shared_destroy)(SharedPayloadBase*); + }; + + bool IsShared() const { + return vtable_ != nullptr && vtable_->shared_destroy != nullptr; + } + + // If the implementation uses a listener, call that. + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) { + return P::Get(m).MatchAndExplain(value, listener->stream()); + } + + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener)) { + return P::Get(m).MatchAndExplain(value, listener); + } + + template + static void DescribeImpl(const MatcherBase& m, std::ostream* os, + bool negation) { + if (negation) { + P::Get(m).DescribeNegationTo(os); + } else { + P::Get(m).DescribeTo(os); + } + } + + template + static const MatcherDescriberInterface* GetDescriberImpl( + const MatcherBase& m) { + // If the impl is a MatcherDescriberInterface, then return it. + // Otherwise use MatcherBase itself. + // This allows us to implement the GetDescriber() function without support + // from the impl, but some users really want to get their impl back when + // they call GetDescriber(). + // We use std::get on a tuple as a workaround of not having `if constexpr`. + return std::get<( + std::is_convertible::value + ? 1 + : 0)>(std::make_tuple(&m, &P::Get(m))); + } + + template + const VTable* GetVTable() { + static constexpr VTable kVTable = {&MatchAndExplainImpl

, + &DescribeImpl

, &GetDescriberImpl

, + P::shared_destroy}; + return &kVTable; + } + + union Buffer { + // Add some types to give Buffer some common alignment/size use cases. + void* ptr; + double d; + int64_t i; + // And add one for the out-of-line cases. + SharedPayloadBase* shared; + }; + + void Destroy() { + if (IsShared() && buffer_.shared->Unref()) { + vtable_->shared_destroy(buffer_.shared); + } + } + + template + static constexpr bool IsInlined() { + return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) && + std::is_trivially_copy_constructible::value && + std::is_trivially_destructible::value; + } + + template ()> + struct ValuePolicy { + static const M& Get(const MatcherBase& m) { + // When inlined along with Init, need to be explicit to avoid violating + // strict aliasing rules. + const M *ptr = static_cast( + static_cast(&m.buffer_)); + return *ptr; + } + static void Init(MatcherBase& m, M impl) { + ::new (static_cast(&m.buffer_)) M(impl); + } + static constexpr auto shared_destroy = nullptr; + }; + + template + struct ValuePolicy { + using Shared = SharedPayload; + static const M& Get(const MatcherBase& m) { + return static_cast(m.buffer_.shared)->value; + } + template + static void Init(MatcherBase& m, Arg&& arg) { + m.buffer_.shared = new Shared(std::forward(arg)); + } + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + struct ValuePolicy*, B> { + using M = const MatcherInterface; + using Shared = SharedPayload>; + static const M& Get(const MatcherBase& m) { + return *static_cast(m.buffer_.shared)->value; + } + static void Init(MatcherBase& m, M* impl) { + m.buffer_.shared = new Shared(std::unique_ptr(impl)); + } + + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + void Init(M&& m) { + using MM = typename std::decay::type; + using Policy = ValuePolicy; + vtable_ = GetVTable(); + Policy::Init(*this, std::forward(m)); + } + + const VTable* vtable_; + Buffer buffer_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a std::shared_ptr to const +// MatcherInterface. Don't inherit from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + explicit Matcher() {} // NOLINT + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template + explicit Matcher( + const MatcherInterface* impl, + typename std::enable_if::value>::type* = + nullptr) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) : internal::MatcherBase(std::forward(m)) {} // NOLINT + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) { + } + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +// Prints a matcher in a human-readable format. +template +std::ostream& operator<<(std::ostream& os, const Matcher& matcher) { + matcher.DescribeTo(&os); + return os; +} + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); } + + void DescribeNegationTo(::std::ostream* os) const override { + impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + }; + + Impl impl_; +}; + +// Creates a matcher from its implementation. +// DEPRECATED: Especially in the generic code, prefer: +// Matcher(new MyMatcherImpl(...)); +// +// MakeMatcher may create a Matcher that accepts its argument by value, which +// leads to unnecessary copies & lack of support for non-copyable types. +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +namespace internal { +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +template +class ComparisonBase { + public: + explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} + + using is_gtest_matcher = void; + + template + bool MatchAndExplain(const Lhs& lhs, std::ostream*) const { + return Op()(lhs, Unwrap(rhs_)); + } + void DescribeTo(std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + void DescribeNegationTo(std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + + private: + template + static const T& Unwrap(const T& v) { + return v; + } + template + static const T& Unwrap(std::reference_wrapper v) { + return v; + } + + Rhs rhs_; +}; + +template +class EqMatcher : public ComparisonBase, Rhs, AnyEq> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyEq>(rhs) { } + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template +class NeMatcher : public ComparisonBase, Rhs, AnyNe> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyNe>(rhs) { } + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template +class LtMatcher : public ComparisonBase, Rhs, AnyLt> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLt>(rhs) { } + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template +class GtMatcher : public ComparisonBase, Rhs, AnyGt> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGt>(rhs) { } + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template +class LeMatcher : public ComparisonBase, Rhs, AnyLe> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLe>(rhs) { } + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template +class GeMatcher : public ComparisonBase, Rhs, AnyGe> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGe>(rhs) { } + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +template ::value>::type> +using StringLike = T; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + return MatchAndExplain(std::string(s), listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(std::string(s), listener); + } + + // Matches anything that can convert to std::string. + // + // This is a template, not just a plain function with const std::string&, + // because absl::string_view has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const std::string& s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) + : RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const std::shared_ptr regex_; + const bool full_match_; +}; +} // namespace internal + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +template +PolymorphicMatcher MatchesRegex( + const internal::StringLike& regex) { + return MatchesRegex(new internal::RE(std::string(regex))); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +template +PolymorphicMatcher ContainsRegex( + const internal::StringLike& regex) { + return ContainsRegex(new internal::RE(std::string(regex))); +} + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template +inline internal::EqMatcher Eq(T x) { return internal::EqMatcher(x); } + +// Constructs a Matcher from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template +Matcher::Matcher(T value) { *this = Eq(value); } + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq(x) is just a convenient short-hand for Matcher(Eq(x)) +// or Matcher(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher(Lt(5)) to be explicit about the type, +// for example. +template +inline Matcher TypedEq(const Rhs& rhs) { return Eq(rhs); } + +// Creates a polymorphic matcher that matches anything >= x. +template +inline internal::GeMatcher Ge(Rhs x) { + return internal::GeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template +inline internal::GtMatcher Gt(Rhs x) { + return internal::GtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template +inline internal::LeMatcher Le(Rhs x) { + return internal::LeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template +inline internal::LtMatcher Lt(Rhs x) { + return internal::LtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template +inline internal::NeMatcher Ne(Rhs x) { + return internal::NeMatcher(x); +} +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, + Matcher matcher, const char* file, + int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test) override; +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher MakeDeathTestMatcher( + Matcher matcher) { + return matcher; +} + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create( \ + #statement, \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != nullptr) { \ + std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \ + : fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// The final parameter to each of these macros is a matcher applied to any data +// the sub-process wrote to stderr. For compatibility with existing tests, a +// bare string is interpreted as a regular expression matcher. +// +// On the regular expressions used in death tests: +// +// GOOGLETEST_CM0005 DO NOT DELETE +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// + +// Asserts that a given `statement` causes the program to exit, with an +// integer exit status that satisfies `predicate`, and emitting error output +// that matches `matcher`. +# define ASSERT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_) + +// Like `ASSERT_EXIT`, but continues on to successive tests in the +// test suite, if any: +# define EXPECT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given `statement` causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches `matcher`. +# define ASSERT_DEATH(statement, matcher) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Like `ASSERT_DEATH`, but continues on to successive tests in the +// test suite, if any: +# define EXPECT_DEATH(statement, matcher) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + ExitedWithCode(const ExitedWithCode&) = default; + void operator=(const ExitedWithCode& other) = delete; + bool operator()(int exit_status) const; + private: + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Tests that an exit code describes an exit due to termination by a +// given signal. +// GOOGLETEST_CM0006 DO NOT DELETE +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters +// on systems that support death tests. This allows one to write such a macro on +// a system that does not support death tests and be sure that it will compile +// on a death-test supporting system. It is exposed publicly so that systems +// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST +// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and +// ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter if and only if EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) +// +// GOOGLETEST_CM0001 DO NOT DELETE +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test suite +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more than once) the first argument to the +// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the +// actual test suite name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests +// in the given test suite, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_SUITE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include +#include + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Type and function utilities for implementing parameterized tests. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure, // Failed and the test should be terminated. + kSkip // Skipped. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, const char* a_file_name, int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == nullptr ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) {} + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? nullptr : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true if and only if the test part was skipped. + bool skipped() const { return type_ == kSkip; } + + // Returns true if and only if the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true if and only if the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true if and only if the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + // Returns true if and only if the test part failed. + bool failed() const { return fatally_failed() || nonfatally_failed(); } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class GTEST_API_ TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + ~HasNewFatalFailureHelper() override; + void ReportTestPartResult(const TestPartResult& result) override; + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +namespace testing { +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) : + param(a_param), + index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template + std::string operator()(const TestParamInfo& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// Utility Functions + +// Outputs a message explaining invalid registration of different +// fixture class for the same test suite. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location); + +template class ParamGeneratorInterface; +template class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + std::unique_ptr > impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + std::shared_ptr > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + ~RangeGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, begin_, 0, step_); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + value_ = static_cast(value_ + step_); + index_++; + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const T* Current() const override { return &value_; } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast(i + step)) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + ~ValuesInIteratorRangeGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, container_.begin()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++iterator_; + value_.reset(); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + const T* Current() const override { + if (value_.get() == nullptr) value_.reset(new T(*iterator_)); + return value_.get(); + } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of std::unique_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable std::unique_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template +std::string DefaultParamName(const TestParamInfo& info) { + Message name_stream; + name_stream << info.index; + return name_stream.GetString(); +} + +template +void TestNotEmpty() { + static_assert(sizeof(T) == 0, "Empty arguments are not allowed."); +} +template +void TestNotEmpty(const T&) {} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + Test* CreateTest() override { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestSuiteInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + using ParamType = typename TestSuite::ParamType; + + TestMetaFactory() {} + + TestFactoryBase* CreateTestFactory(ParamType parameter) override { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfoBase is a generic interface +// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds +// a collection of pointers to the ParameterizedTestSuiteInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestSuiteInfoBase { + public: + virtual ~ParameterizedTestSuiteInfoBase() {} + + // Base part of test suite name for display purposes. + virtual const std::string& GetTestSuiteName() const = 0; + // Test suite id to verify identity. + virtual TypeId GetTestSuiteTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test suite right before running them in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestSuiteInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Report a the name of a test_suit as safe to ignore +// as the side effect of construction of this type. +struct GTEST_API_ MarkAsIgnored { + explicit MarkAsIgnored(const char* test_suite); +}; + +GTEST_API_ void InsertSyntheticTestCase(const std::string& name, + CodeLocation location, bool has_test_p); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test suite and generators +// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that +// test suite. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestSuiteInstantiation(). + using ParamType = typename TestSuite::ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + using ParamNameGeneratorFunc = std::string(const TestParamInfo&); + + explicit ParameterizedTestSuiteInfo(const char* name, + CodeLocation code_location) + : test_suite_name_(name), code_location_(code_location) {} + + // Test suite base name for display purposes. + const std::string& GetTestSuiteName() const override { + return test_suite_name_; + } + // Test suite id to verify identity. + TypeId GetTestSuiteTypeId() const override { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_suite_name is the base name of the test suite (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test suite base name and DoBar is test base name. + void AddTestPattern(const char* test_suite_name, const char* test_base_name, + TestMetaFactoryBase* meta_factory, + CodeLocation code_location) { + tests_.push_back(std::shared_ptr(new TestInfo( + test_suite_name, test_base_name, meta_factory, code_location))); + } + // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestSuiteInstantiation(const std::string& instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.push_back( + InstantiationInfo(instantiation_name, func, name_func, file, line)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test suite + // right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more than once. + void RegisterTests() override { + bool generated_instantiations = false; + + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + std::shared_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const std::string& instantiation_name = gen_it->name; + ParamGenerator generator((*gen_it->generator)()); + ParamNameGeneratorFunc* name_func = gen_it->name_func; + const char* file = gen_it->file; + int line = gen_it->line; + + std::string test_suite_name; + if ( !instantiation_name.empty() ) + test_suite_name = instantiation_name + "/"; + test_suite_name += test_info->test_suite_base_name; + + size_t i = 0; + std::set test_param_names; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + generated_instantiations = true; + + Message test_name_stream; + + std::string param_name = name_func( + TestParamInfo(*param_it, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid, in " << file + << " line " << line << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name + << "', in " << file << " line " << line << std::endl; + + test_param_names.insert(param_name); + + if (!test_info->test_base_name.empty()) { + test_name_stream << test_info->test_base_name << "/"; + } + test_name_stream << param_name; + MakeAndRegisterTestInfo( + test_suite_name.c_str(), test_name_stream.GetString().c_str(), + nullptr, // No type parameter. + PrintToString(*param_it).c_str(), test_info->code_location, + GetTestSuiteTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_, + !tests_.empty()); + } + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory, + CodeLocation a_code_location) + : test_suite_base_name(a_test_suite_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory), + code_location(a_code_location) {} + + const std::string test_suite_base_name; + const std::string test_base_name; + const std::unique_ptr > test_meta_factory; + const CodeLocation code_location; + }; + using TestInfoContainer = ::std::vector >; + // Records data received from INSTANTIATE_TEST_SUITE_P macros: + // + struct InstantiationInfo { + InstantiationInfo(const std::string &name_in, + GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, + const char* file_in, + int line_in) + : name(name_in), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) + return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!IsAlNum(name[index]) && name[index] != '_') + return false; + } + + return true; + } + + const std::string test_suite_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo); +}; // class ParameterizedTestSuiteInfo + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +template +using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteRegistry contains a map of +// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P +// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding +// ParameterizedTestSuiteInfo descriptors. +class ParameterizedTestSuiteRegistry { + public: + ParameterizedTestSuiteRegistry() {} + ~ParameterizedTestSuiteRegistry() { + for (auto& test_suite_info : test_suite_infos_) { + delete test_suite_info; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test suite. + template + ParameterizedTestSuiteInfo* GetTestSuitePatternHolder( + const char* test_suite_name, CodeLocation code_location) { + ParameterizedTestSuiteInfo* typed_test_info = nullptr; + for (auto& test_suite_info : test_suite_infos_) { + if (test_suite_info->GetTestSuiteName() == test_suite_name) { + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name, code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestSuiteInfo >(test_suite_info); + } + break; + } + } + if (typed_test_info == nullptr) { + typed_test_info = new ParameterizedTestSuiteInfo( + test_suite_name, code_location); + test_suite_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (auto& test_suite_info : test_suite_infos_) { + test_suite_info->RegisterTests(); + } + } +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder(test_case_name, code_location); + } + +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + private: + using TestSuiteInfoContainer = ::std::vector; + + TestSuiteInfoContainer test_suite_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry); +}; + +// Keep track of what type-parameterized test suite are defined and +// where as well as which are intatiated. This allows susequently +// identifying suits that are defined but never used. +class TypeParameterizedTestSuiteRegistry { + public: + // Add a suite definition + void RegisterTestSuite(const char* test_suite_name, + CodeLocation code_location); + + // Add an instantiation of a suit. + void RegisterInstantiation(const char* test_suite_name); + + // For each suit repored as defined but not reported as instantiation, + // emit a test that reports that fact (configurably, as an error). + void CheckForInstantiations(); + + private: + struct TypeParameterizedTestSuiteInfo { + explicit TypeParameterizedTestSuiteInfo(CodeLocation c) + : code_location(c), instantiated(false) {} + + CodeLocation code_location; + bool instantiated; + }; + + std::map suites_; +}; + +} // namespace internal + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { +// Used in the Values() function to provide polymorphic capabilities. + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + +template +class ValueArray { + public: + explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {} + + template + operator ParamGenerator() const { // NOLINT + return ValuesIn(MakeVector(MakeIndexSequence())); + } + + private: + template + std::vector MakeVector(IndexSequence) const { + return std::vector{static_cast(v_.template Get())...}; + } + + FlatTuple v_; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template +class CartesianProductGenerator + : public ParamGeneratorInterface<::std::tuple> { + public: + typedef ::std::tuple ParamType; + + CartesianProductGenerator(const std::tuple...>& g) + : generators_(g) {} + ~CartesianProductGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generators_, false); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generators_, true); + } + + private: + template + class IteratorImpl; + template + class IteratorImpl> + : public ParamIteratorInterface { + public: + IteratorImpl(const ParamGeneratorInterface* base, + const std::tuple...>& generators, bool is_end) + : base_(base), + begin_(std::get(generators).begin()...), + end_(std::get(generators).end()...), + current_(is_end ? end_ : begin_) { + ComputeCurrentValue(); + } + ~IteratorImpl() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + void Advance() override { + assert(!AtEnd()); + // Advance the last iterator. + ++std::get(current_); + // if that reaches end, propagate that up. + AdvanceIfEnd(); + ComputeCurrentValue(); + } + ParamIteratorInterface* Clone() const override { + return new IteratorImpl(*this); + } + + const ParamType* Current() const override { return current_value_.get(); } + + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const IteratorImpl* typed_other = + CheckedDowncastToActualType(&other); + + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + if (AtEnd() && typed_other->AtEnd()) return true; + + bool same = true; + bool dummy[] = { + (same = same && std::get(current_) == + std::get(typed_other->current_))...}; + (void)dummy; + return same; + } + + private: + template + void AdvanceIfEnd() { + if (std::get(current_) != std::get(end_)) return; + + bool last = ThisI == 0; + if (last) { + // We are done. Nothing else to propagate. + return; + } + + constexpr size_t NextI = ThisI - (ThisI != 0); + std::get(current_) = std::get(begin_); + ++std::get(current_); + AdvanceIfEnd(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = std::make_shared(*std::get(current_)...); + } + bool AtEnd() const { + bool at_end = false; + bool dummy[] = { + (at_end = at_end || std::get(current_) == std::get(end_))...}; + (void)dummy; + return at_end; + } + + const ParamGeneratorInterface* const base_; + std::tuple::iterator...> begin_; + std::tuple::iterator...> end_; + std::tuple::iterator...> current_; + std::shared_ptr current_value_; + }; + + using Iterator = IteratorImpl::type>; + + std::tuple...> generators_; +}; + +template +class CartesianProductHolder { + public: + CartesianProductHolder(const Gen&... g) : generators_(g...) {} + template + operator ParamGenerator<::std::tuple>() const { + return ParamGenerator<::std::tuple>( + new CartesianProductGenerator(generators_)); + } + + private: + std::tuple generators_; +}; + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test suite is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test suite FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test suite StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test suite StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_SUITE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_SUITE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename std::iterator_traits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename std::iterator_traits::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test suite BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_SUITE_P(NumSequence, +// BarTest, +// Values("one", "two", "three")); +// +// This instantiates tests from test suite BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// +template +internal::ValueArray Values(T... v) { + return internal::ValueArray(std::move(v)...); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test suite FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// std::tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// std::tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder Combine(const Generator&... g) { + return internal::CartesianProductHolder(g...); +} + +#define TEST_P(test_suite_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + void TestBody() override; \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify +// generator and an optional function or functor that generates custom test name +// suffixes based on the test parameters. Such a function or functor should +// accept one argument of type testing::TestParamInfo, and +// return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define GTEST_EXPAND_(arg) arg +#define GTEST_GET_FIRST_(first, ...) first +#define GTEST_GET_SECOND_(first, second, ...) second + +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))))(info); \ + } \ + static int gtest_##prefix##test_suite_name##_dummy_ \ + GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + + +// Allow Marking a Parameterized test class as not needing to be instantiated. +#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \ + namespace gtest_do_not_use_outside_namespace_scope {} \ + static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \ + GTEST_STRINGIFY_(T)) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TEST_CASE_P \ + static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \ + ""); \ + INSTANTIATE_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +// Google C++ Testing and Mocking Framework definitions useful in production code. +// GOOGLETEST_CM0003 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test suite, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_SUITE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_SUITE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test suite as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +// TYPED_TEST_SUITE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template +// static std::string GetName(int) { +// if (std::is_same()) return "char"; +// if (std::is_same()) return "int"; +// if (std::is_same()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test suite +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_SUITE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test suite as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test suite name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test suite name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_SUITE above, +// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames); + +#endif // 0 + + +// Implements typed tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test suite. +#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_ + +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestSuiteName) \ + gtest_type_params_##TestSuiteName##_NameGenerator + +#define TYPED_TEST_SUITE(CaseName, Types, ...) \ + typedef ::testing::internal::GenerateTypeList::type \ + GTEST_TYPE_PARAMS_(CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +#define TYPED_TEST(CaseName, TestName) \ + static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \ + "test-name must not be empty"); \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##CaseName##_##TestName##_registered_ \ + GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + GTEST_STRINGIFY_(CaseName), \ + GTEST_STRINGIFY_(TestName), 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)::TestBody() + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE \ + static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \ + TYPED_TEST_SUITE +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Implements type-parameterized tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test suite are defined in. The exact +// name of the namespace is subject to change without notice. +#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test suite. +#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \ + gtest_typed_test_suite_p_state_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test suite. +#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \ + gtest_registered_test_names_##TestSuiteName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +#define TYPED_TEST_SUITE_P(SuiteName) \ + static ::testing::internal::TypedTestSuitePState \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE_P \ + static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \ + TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template \ + class TestName : public SuiteName { \ + private: \ + typedef SuiteName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template \ + void GTEST_SUITE_NAMESPACE_( \ + SuiteName)::TestName::TestBody() + +// Note: this won't work correctly if the trailing arguments are macros. +#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_( \ + SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ + GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define REGISTER_TYPED_TEST_CASE_P \ + static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \ + ""); \ + REGISTER_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ + static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ + "test-suit-prefix must not be empty"); \ + static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestSuite< \ + SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ + ::testing::internal::GenerateTypeList::type>:: \ + Register(GTEST_STRINGIFY_(Prefix), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \ + GTEST_STRINGIFY_(SuiteName), \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::GenerateTypeList::type>()) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TYPED_TEST_CASE_P \ + static_assert( \ + ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \ + INSTANTIATE_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4805) +# pragma warning(disable:4100) +#endif + + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag controls whether the test runner should continue execution past +// first failure. +GTEST_DECLARE_bool_(fail_fast); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints only test failures. +GTEST_DECLARE_bool_(brief); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); +std::set* GetIgnoredParameterizedTestSuites(); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestSuite; + +// Old API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TestCase = TestSuite; +#endif +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +// C4800 is a level 3 warning in Visual Studio 2015 and earlier. +// This warning is not emitted in Visual Studio 2017. +// This warning is off by default starting in Visual Studio 2019 but can be +// enabled with command-line options. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename std::enable_if< + !std::is_convertible::value>::type* + /*enabler*/ + = nullptr) + : success_(success) {} + +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true if and only if the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != nullptr ? message_->c_str() : ""; + } + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == nullptr) message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + std::unique_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +// Includes the auto-generated header that implements a family of generic +// predicate assertion macros. This include comes late because it relies on +// APIs declared above. +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 01/02/2019 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ", " << e5 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n" + << e5 << " evaluates to " << ::testing::PrintToString(v5); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +namespace testing { + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestSuites, and +// each TestSuite contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::SetUpTestSuite() before running the first + // test in test suite Foo. Hence a sub-class can define its own + // SetUpTestSuite() method to shadow the one defined in the super + // class. + static void SetUpTestSuite() {} + + // Tears down the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::TearDownTestSuite() after running the last + // test in test suite Foo. Hence a sub-class can define its own + // TearDownTestSuite() method to shadow the one defined in the super + // class. + static void TearDownTestSuite() {} + + // Legacy API is deprecated but still available. Use SetUpTestSuite and + // TearDownTestSuite instead. +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + static void TearDownTestCase() {} + static void SetUpTestCase() {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns true if and only if the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true if and only if the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true if and only if the current test was skipped. + static bool IsSkipped(); + + // Returns true if and only if the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test suite, or for the entire + // invocation of the test program when used outside of the context of a + // test suite. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestSuite or TearDownTestSuite are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true if and only if the current test has the same fixture class + // as the first test in the current test suite. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const std::unique_ptr gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true if and only if the test passed (i.e. no test part failed). + bool Passed() const { return !Skipped() && !Failed(); } + + // Returns true if and only if the test was skipped. + bool Skipped() const; + + // Returns true if and only if the test failed. + bool Failed() const; + + // Returns true if and only if the test fatally failed. + bool HasFatalFailure() const; + + // Returns true if and only if the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test case start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestSuite; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the start time. + void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testsuite tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properties_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test suite name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test suite name. + const char* test_suite_name() const { return test_suite_name_.c_str(); } + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const char* test_case_name() const { return test_suite_name(); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != nullptr) return value_param_->c_str(); + return nullptr; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test suite Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true if and only if this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestSuite; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, internal::CodeLocation code_location, + internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_suite_name, const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Skip and records the test result for this object. + void Skip(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_suite_name_; // test suite name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const std::unique_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True if and only if this test should run + bool is_disabled_; // True if and only if this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test suite, which consists of a vector of TestInfos. +// +// TestSuite is not copyable. +class GTEST_API_ TestSuite { + public: + // Creates a TestSuite with the given name. + // + // TestSuite does NOT have a default constructor. Always use this + // constructor to create a TestSuite object. + // + // Arguments: + // + // name: name of the test suite + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite(const char* name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + + // Destructor of TestSuite. + virtual ~TestSuite(); + + // Gets the name of the TestSuite. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test suite. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns true if any test in this test suite should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test suite. + int successful_test_count() const; + + // Gets the number of skipped tests in this test suite. + int skipped_test_count() const; + + // Gets the number of failed tests in this test suite. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test suite. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test suite that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test suite. + int total_test_count() const; + + // Returns true if and only if the test suite passed. + bool Passed() const { return !Failed(); } + + // Returns true if and only if the test suite failed. + bool Failed() const { + return failed_test_count() > 0 || ad_hoc_test_result().Failed(); + } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test suite start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestSuite and TearDownTestSuite. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestSuite. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestSuite. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test suite. Will delete the TestInfo upon + // destruction of the TestSuite object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test suite. + void ClearResult(); + + // Clears the results of all tests in the given test suite. + static void ClearTestSuiteResult(TestSuite* test_suite) { + test_suite->ClearResult(); + } + + // Runs every test in this TestSuite. + void Run(); + + // Skips the execution of tests under this TestSuite + void Skip(); + + // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed + // for catching exceptions thrown from SetUpTestSuite(). + void RunSetUpTestSuite() { + if (set_up_tc_ != nullptr) { + (*set_up_tc_)(); + } + } + + // Runs TearDownTestSuite() for this TestSuite. This wrapper is + // needed for catching exceptions thrown from TearDownTestSuite(). + void RunTearDownTestSuite() { + if (tear_down_tc_ != nullptr) { + (*tear_down_tc_)(); + } + } + + // Returns true if and only if test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true if and only if test skipped. + static bool TestSkipped(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Skipped(); + } + + // Returns true if and only if test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true if and only if the test is disabled and will be reported in + // the XML report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true if and only if test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if and only if this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test suite. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test suite. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test suite. + internal::SetUpTestSuiteFunc set_up_tc_; + // Pointer to the function that tears down the test suite. + internal::TearDownTestSuiteFunc tear_down_tc_; + // True if and only if any test in this test suite should run. + bool should_run_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestSuite and + // TearDownTestSuite. + TestResult ad_hoc_test_result_; + + // We disallow copying TestSuites. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestSuite); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test suite starts. + virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {} + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test suite ends. + virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} + void OnTestEnd(const TestInfo& /*test_info*/) override {} + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestSuite; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestSuites. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestSuite object for the test that's currently running, + // or NULL if no test is running. + const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_); + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_); +#endif + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + int successful_test_case_count() const; + int failed_test_case_count() const; + int total_test_case_count() const; + int test_case_to_run_count() const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const; + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test suites. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked + // from SetUpTestSuite or TearDownTestSuite, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableTestSuite(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend std::set* internal::GetIgnoredParameterizedTestSuites(); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleTest(); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + return EqFailure(lhs_expression, + rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), + false); +} + +// This block of code defines operator==/!= +// to block lexical scope lookup. +// It prevents using invalid operator==/!= defined at namespace scope. +struct faketype {}; +inline bool operator==(faketype, faketype) { return true; } +inline bool operator!=(faketype, faketype) { return false; } + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +class EqHelper { + public: + // This templatized version is for the general case. + template < + typename T1, typename T2, + // Disable this overload for cases where one argument is a pointer + // and the other is the null pointer constant. + typename std::enable_if::value || + !std::is_pointer::value>::type* = nullptr> + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, + BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + template + static AssertionResult Compare( + const char* lhs_expression, const char* rhs_expression, + // Handle cases where '0' is used as a null pointer literal. + std::nullptr_t /* lhs */, T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, static_cast(nullptr), + rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\ + }\ +} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, + RawType rhs_value) { + const FloatingPoint lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << rhs_value; + + return EqFailure(lhs_expression, + rhs_expression, + StringStreamToString(&lhs_ss), + StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// ~FooTest() override { +// // Can use GetParam() here. +// } +// void SetUp() override { +// // Can use GetParam() here. +// } +// void TearDown override { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. + static const ParamType& GetParam() { + GTEST_CHECK_(parameter_ != nullptr) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = nullptr; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +// Macros for indicating success/failure in test code. + +// Skips test in runtime. +// Skipping test aborts current function. +// Skipped tests are neither successful nor failed. +#define GTEST_SKIP() GTEST_SKIP_("") + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Like GTEST_FAIL(), but at the given source file location. +#define GTEST_FAIL_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define GTEST_EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define GTEST_ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Define these macros to 1 to omit the definition of the corresponding +// EXPECT or ASSERT, which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_EXPECT_TRUE +#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_EXPECT_FALSE +#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_TRUE +#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_FALSE +#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) +#endif + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles if and only if type1 and type2 +// are the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +constexpr bool StaticAssertTypeEq() noexcept { + static_assert(std::is_same::value, "T1 and T2 are not the same type"); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test suite, and the second +// parameter is the name of the test within the test suite. +// +// The convention is to end the test suite name with "Test". For +// example, a test suite for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ + ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test suite name. The second parameter is the +// name of the test within the test suite. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } +// +// GOOGLETEST_CM0011 DO NOT DELETE +#if !GTEST_DONT_DEFINE_TEST +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) +#endif // !GTEST_DONT_DEFINE_TEST + +// Returns a path to temporary directory. +// Tries to determine an appropriate directory for the platform. +GTEST_API_ std::string TempDir(); + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +// Dynamically registers a test with the framework. +// +// This is an advanced API only to be used when the `TEST` macros are +// insufficient. The macros should be preferred when possible, as they avoid +// most of the complexity of calling this function. +// +// The `factory` argument is a factory callable (move-constructible) object or +// function pointer that creates a new instance of the Test object. It +// handles ownership to the caller. The signature of the callable is +// `Fixture*()`, where `Fixture` is the test fixture class for the test. All +// tests registered with the same `test_suite_name` must return the same +// fixture type. This is checked at runtime. +// +// The framework will infer the fixture class from the factory and will call +// the `SetUpTestSuite` and `TearDownTestSuite` for it. +// +// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is +// undefined. +// +// Use case example: +// +// class MyFixture : public ::testing::Test { +// public: +// // All of these optional, just like in regular macro usage. +// static void SetUpTestSuite() { ... } +// static void TearDownTestSuite() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } +// }; +// +// class MyTest : public MyFixture { +// public: +// explicit MyTest(int data) : data_(data) {} +// void TestBody() override { ... } +// +// private: +// int data_; +// }; +// +// void RegisterMyTests(const std::vector& values) { +// for (int v : values) { +// ::testing::RegisterTest( +// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, +// std::to_string(v).c_str(), +// __FILE__, __LINE__, +// // Important to use the fixture type as the return type here. +// [=]() -> MyFixture* { return new MyTest(v); }); +// } +// } +// ... +// int main(int argc, char** argv) { +// std::vector values_to_test = LoadValuesFromConfig(); +// RegisterMyTests(values_to_test); +// ... +// return RUN_ALL_TESTS(); +// } +// +template +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory) { + using TestT = typename std::remove_pointer::type; + + class FactoryImpl : public internal::TestFactoryBase { + public: + explicit FactoryImpl(Factory f) : factory_(std::move(f)) {} + Test* CreateTest() override { return factory_(); } + + private: + Factory factory_; + }; + + return internal::MakeAndRegisterTestInfo( + test_suite_name, test_name, type_param, value_param, + internal::CodeLocation(file, line), internal::GetTypeId(), + internal::SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + internal::SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + new FactoryImpl{std::move(factory)}); +} + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/test/gtest/gtest_main.cc b/test/gtest/gtest_main.cc new file mode 100644 index 0000000000000000000000000000000000000000..46b27c3d7d5654668906bff5f5ba1a266881f77b --- /dev/null +++ b/test/gtest/gtest_main.cc @@ -0,0 +1,54 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "gtest/gtest.h" + +#if GTEST_OS_ESP8266 || GTEST_OS_ESP32 +#if GTEST_OS_ESP8266 +extern "C" { +#endif +void setup() { + testing::InitGoogleTest(); +} + +void loop() { RUN_ALL_TESTS(); } + +#if GTEST_OS_ESP8266 +} +#endif + +#else + +GTEST_API_ int main(int argc, char **argv) { + printf("Running main() from %s\n", __FILE__); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/image.jpg b/test/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f74b495328f3cf26a12dd2c4843484cc6a13561c Binary files /dev/null and b/test/image.jpg differ diff --git a/test/include_httplib.cc b/test/include_httplib.cc new file mode 100644 index 0000000000000000000000000000000000000000..fd38cb8289c5a12cb9869e53e17091be891669f5 --- /dev/null +++ b/test/include_httplib.cc @@ -0,0 +1,5 @@ +// The sole purpose of this file is to include httplib.h in a separate +// compilation unit, thus verifying that inline keywords have not been forgotten +// when linked together with test.cc. + +#include diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..52fb8251dba92ac4e150b80ef76c34fbb048dbe1 --- /dev/null +++ b/test/meson.build @@ -0,0 +1,141 @@ +# SPDX-FileCopyrightText: 2021 Andrea Pappacoda +# +# SPDX-License-Identifier: MIT + +gtest_dep = dependency('gtest', main: true) +openssl = find_program('openssl') +test_conf = files('test.conf') + +key_pem = custom_target( + 'key_pem', + output: 'key.pem', + command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048'] +) + +temp_req = custom_target( + 'temp_req', + input: key_pem, + output: 'temp_req', + command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@'] +) + +cert_pem = custom_target( + 'cert_pem', + input: [temp_req, key_pem], + output: 'cert.pem', + command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '3650', '-req', '-signkey', '@INPUT1@', '-out', '@OUTPUT@'] +) + +cert2_pem = custom_target( + 'cert2_pem', + input: key_pem, + output: 'cert2.pem', + command: [openssl, 'req', '-x509', '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN'] +) + +key_encrypted_pem = custom_target( + 'key_encrypted_pem', + output: 'key_encrypted.pem', + command: [openssl, 'genrsa', '-passout', 'pass:test123!', '-out', '@OUTPUT@', '2048'] +) + +cert_encrypted_pem = custom_target( + 'cert_encrypted_pem', + input: key_encrypted_pem, + output: 'cert_encrypted.pem', + command: [openssl, 'req', '-x509', '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN'] +) + +rootca_key_pem = custom_target( + 'rootca_key_pem', + output: 'rootCA.key.pem', + command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048'] +) + +rootca_cert_pem = custom_target( + 'rootca_cert_pem', + input: rootca_key_pem, + output: 'rootCA.cert.pem', + command: [openssl, 'req', '-x509', '-new', '-batch', '-config', files('test.rootCA.conf'), '-key', '@INPUT@', '-days', '1024', '-out', '@OUTPUT@'] +) + +client_key_pem = custom_target( + 'client_key_pem', + output: 'client.key.pem', + command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048'] +) + +client_temp_req = custom_target( + 'client_temp_req', + input: client_key_pem, + output: 'client_temp_req', + command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@'] +) + +client_cert_pem = custom_target( + 'client_cert_pem', + input: [client_temp_req, rootca_cert_pem, rootca_key_pem], + output: 'client.cert.pem', + command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@'] +) + +client_encrypted_key_pem = custom_target( + 'client_encrypted_key_pem', + output: 'client_encrypted.key.pem', + command: [openssl, 'genrsa', '-aes256', '-passout', 'pass:test012!', '-out', '@OUTPUT@', '2048'] +) + +client_encrypted_temp_req = custom_target( + 'client_encrypted_temp_req', + input: client_encrypted_key_pem, + output: 'client_encrypted_temp_req', + command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-passin', 'pass:test012!', '-out', '@OUTPUT@'] +) + +client_encrypted_cert_pem = custom_target( + 'client_encrypted_cert_pem', + input: [client_encrypted_temp_req, rootca_cert_pem, rootca_key_pem], + output: 'client_encrypted.cert.pem', + command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@'] +) + +# Copy test files to the build directory +configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true) +configure_file(input: 'image.jpg', output: 'image.jpg', copy: true) +subdir(join_paths('www', 'dir')) +subdir(join_paths('www2', 'dir')) +subdir(join_paths('www3', 'dir')) + +# GoogleTest 1.13.0 requires C++14 +test_options = [] +if gtest_dep.version().version_compare('>=1.13.0') + test_options += 'cpp_std=c++14' +endif + +test( + 'main', + executable( + 'main', + 'test.cc', + dependencies: [ + cpp_httplib_dep, + gtest_dep + ], + override_options: test_options + ), + depends: [ + key_pem, + cert_pem, + cert2_pem, + key_encrypted_pem, + cert_encrypted_pem, + rootca_key_pem, + rootca_cert_pem, + client_key_pem, + client_cert_pem, + client_encrypted_key_pem, + client_encrypted_cert_pem + ], + workdir: meson.current_build_dir(), + timeout: 300 +) diff --git a/test/proxy/Dockerfile b/test/proxy/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cd20938b6a8549b7eab353519893db17b2c267c5 --- /dev/null +++ b/test/proxy/Dockerfile @@ -0,0 +1,13 @@ +FROM centos:7 + +ARG auth="basic" +ARG port="3128" + +RUN yum install -y squid + +COPY ./${auth}_squid.conf /etc/squid/squid.conf +COPY ./${auth}_passwd /etc/squid/passwd + +EXPOSE ${port} + +CMD ["/usr/sbin/squid", "-N"] diff --git a/test/proxy/basic_passwd b/test/proxy/basic_passwd new file mode 100644 index 0000000000000000000000000000000000000000..bb1b7091e649db771d05dc1a86fb2ae5ce801809 --- /dev/null +++ b/test/proxy/basic_passwd @@ -0,0 +1 @@ +hello:$apr1$O6S28OBL$8dr3ixl4Mohf97hgsYvLy/ diff --git a/test/proxy/basic_squid.conf b/test/proxy/basic_squid.conf new file mode 100644 index 0000000000000000000000000000000000000000..f97f09a4feb5a2253db3b45e499485bb171f2ea6 --- /dev/null +++ b/test/proxy/basic_squid.conf @@ -0,0 +1,81 @@ +# +# Recommended minimum configuration: +# + +# Example rule allowing access from your local networks. +# Adapt to list your (internal) IP networks from where browsing +# should be allowed +acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN) +acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN) +acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN) +acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines +acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN) +acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN) +acl localnet src fc00::/7 # RFC 4193 local private network range +acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + +acl SSL_ports port 443 +acl Safe_ports port 80 # http +acl Safe_ports port 21 # ftp +acl Safe_ports port 443 # https +acl Safe_ports port 70 # gopher +acl Safe_ports port 210 # wais +acl Safe_ports port 1025-65535 # unregistered ports +acl Safe_ports port 280 # http-mgmt +acl Safe_ports port 488 # gss-http +acl Safe_ports port 591 # filemaker +acl Safe_ports port 777 # multiling http +acl CONNECT method CONNECT + +auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd +auth_param basic realm proxy +acl authenticated proxy_auth REQUIRED +http_access allow authenticated + +# +# Recommended minimum Access Permission configuration: +# +# Deny requests to certain unsafe ports +http_access deny !Safe_ports + +# Deny CONNECT to other than secure SSL ports +http_access deny CONNECT !SSL_ports + +# Only allow cachemgr access from localhost +http_access allow localhost manager +http_access deny manager + +# We strongly recommend the following be uncommented to protect innocent +# web applications running on the proxy server who think the only +# one who can access services on "localhost" is a local user +#http_access deny to_localhost + +# +# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS +# + +# Example rule allowing access from your local networks. +# Adapt localnet in the ACL section to list your (internal) IP networks +# from where browsing should be allowed +http_access allow localnet +http_access allow localhost + +# And finally deny all other access to this proxy +http_access deny all + +# Squid normally listens to port 3128 +http_port 3128 + +# Uncomment and adjust the following to add a disk cache directory. +#cache_dir ufs /var/spool/squid 100 16 256 + +# Leave coredumps in the first cache dir +coredump_dir /var/spool/squid + +# +# Add any of your own refresh_pattern entries above these. +# +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern ^gopher: 1440 0% 1440 +refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 +refresh_pattern . 0 20% 4320 diff --git a/test/proxy/digest_passwd b/test/proxy/digest_passwd new file mode 100644 index 0000000000000000000000000000000000000000..d45615f551244917f3374f90774f18f9fe7afb23 --- /dev/null +++ b/test/proxy/digest_passwd @@ -0,0 +1 @@ +hello:world diff --git a/test/proxy/digest_squid.conf b/test/proxy/digest_squid.conf new file mode 100644 index 0000000000000000000000000000000000000000..050c5da1c9fb93e47255460f3cee3613ba5d593d --- /dev/null +++ b/test/proxy/digest_squid.conf @@ -0,0 +1,81 @@ +# +# Recommended minimum configuration: +# + +# Example rule allowing access from your local networks. +# Adapt to list your (internal) IP networks from where browsing +# should be allowed +acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN) +acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN) +acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN) +acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines +acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN) +acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN) +acl localnet src fc00::/7 # RFC 4193 local private network range +acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + +acl SSL_ports port 443 +acl Safe_ports port 80 # http +acl Safe_ports port 21 # ftp +acl Safe_ports port 443 # https +acl Safe_ports port 70 # gopher +acl Safe_ports port 210 # wais +acl Safe_ports port 1025-65535 # unregistered ports +acl Safe_ports port 280 # http-mgmt +acl Safe_ports port 488 # gss-http +acl Safe_ports port 591 # filemaker +acl Safe_ports port 777 # multiling http +acl CONNECT method CONNECT + +auth_param digest program /usr/lib64/squid/digest_file_auth /etc/squid/passwd +auth_param digest realm proxy +acl authenticated proxy_auth REQUIRED +http_access allow authenticated + +# +# Recommended minimum Access Permission configuration: +# +# Deny requests to certain unsafe ports +http_access deny !Safe_ports + +# Deny CONNECT to other than secure SSL ports +http_access deny CONNECT !SSL_ports + +# Only allow cachemgr access from localhost +http_access allow localhost manager +http_access deny manager + +# We strongly recommend the following be uncommented to protect innocent +# web applications running on the proxy server who think the only +# one who can access services on "localhost" is a local user +#http_access deny to_localhost + +# +# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS +# + +# Example rule allowing access from your local networks. +# Adapt localnet in the ACL section to list your (internal) IP networks +# from where browsing should be allowed +http_access allow localnet +http_access allow localhost + +# And finally deny all other access to this proxy +http_access deny all + +# Squid normally listens to port 3128 +http_port 3129 + +# Uncomment and adjust the following to add a disk cache directory. +#cache_dir ufs /var/spool/squid 100 16 256 + +# Leave coredumps in the first cache dir +coredump_dir /var/spool/squid + +# +# Add any of your own refresh_pattern entries above these. +# +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern ^gopher: 1440 0% 1440 +refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 +refresh_pattern . 0 20% 4320 diff --git a/test/proxy/docker-compose.yml b/test/proxy/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..67a9e9b617d808f97e90b0d70a0b1cccfea09eae --- /dev/null +++ b/test/proxy/docker-compose.yml @@ -0,0 +1,22 @@ +version: '2' + +services: + squid_basic: + image: squid_basic + restart: always + ports: + - "3128:3128" + build: + context: ./ + args: + auth: basic + + squid_digest: + image: squid_digest + restart: always + ports: + - "3129:3129" + build: + context: ./ + args: + auth: digest diff --git a/test/test.cc b/test/test.cc new file mode 100644 index 0000000000000000000000000000000000000000..aab9db057ec26b35dd070931cd3c411bc9f82264 --- /dev/null +++ b/test/test.cc @@ -0,0 +1,7390 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SERVER_CERT_FILE "./cert.pem" +#define SERVER_CERT2_FILE "./cert2.pem" +#define SERVER_PRIVATE_KEY_FILE "./key.pem" +#define CA_CERT_FILE "./ca-bundle.crt" +#define CLIENT_CA_CERT_FILE "./rootCA.cert.pem" +#define CLIENT_CA_CERT_DIR "." +#define CLIENT_CERT_FILE "./client.cert.pem" +#define CLIENT_PRIVATE_KEY_FILE "./client.key.pem" +#define CLIENT_ENCRYPTED_CERT_FILE "./client_encrypted.cert.pem" +#define CLIENT_ENCRYPTED_PRIVATE_KEY_FILE "./client_encrypted.key.pem" +#define CLIENT_ENCRYPTED_PRIVATE_KEY_PASS "test012!" +#define SERVER_ENCRYPTED_CERT_FILE "./cert_encrypted.pem" +#define SERVER_ENCRYPTED_PRIVATE_KEY_FILE "./key_encrypted.pem" +#define SERVER_ENCRYPTED_PRIVATE_KEY_PASS "test123!" + +using namespace std; +using namespace httplib; + +const char *HOST = "localhost"; +const int PORT = 1234; + +const string LONG_QUERY_VALUE = string(25000, '@'); +const string LONG_QUERY_URL = "/long-query-value?key=" + LONG_QUERY_VALUE; + +const std::string JSON_DATA = "{\"hello\":\"world\"}"; + +const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB + +MultipartFormData &get_file_value(MultipartFormDataItems &files, + const char *key) { + auto it = std::find_if( + files.begin(), files.end(), + [&](const MultipartFormData &file) { return file.name == key; }); +#ifdef CPPHTTPLIB_NO_EXCEPTIONS + return *it; +#else + if (it != files.end()) { return *it; } + throw std::runtime_error("invalid multipart form data name error"); +#endif +} + +TEST(ConstructorTest, MoveConstructible) { + EXPECT_FALSE(std::is_copy_constructible::value); + EXPECT_TRUE(std::is_nothrow_move_constructible::value); +} + +#ifdef _WIN32 +TEST(StartupTest, WSAStartup) { + WSADATA wsaData; + int ret = WSAStartup(0x0002, &wsaData); + ASSERT_EQ(0, ret); +} +#endif + +TEST(DecodeURLTest, PercentCharacter) { + EXPECT_EQ( + detail::decode_url( + R"(descrip=Gastos%20%C3%A1%C3%A9%C3%AD%C3%B3%C3%BA%C3%B1%C3%91%206)", + false), + R"(descrip=Gastos áéíóúñÑ 6)"); +} + +TEST(DecodeURLTest, PercentCharacterNUL) { + string expected; + expected.push_back('x'); + expected.push_back('\0'); + expected.push_back('x'); + + EXPECT_EQ(detail::decode_url("x%00x", false), expected); +} + +TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest) { + string unescapedCharacters = "-_.!~*'()"; + + EXPECT_EQ(detail::encode_query_param(unescapedCharacters), "-_.!~*'()"); +} + +TEST(EncodeQueryParamTest, ParseReservedCharactersTest) { + string reservedCharacters = ";,/?:@&=+$"; + + EXPECT_EQ(detail::encode_query_param(reservedCharacters), + "%3B%2C%2F%3F%3A%40%26%3D%2B%24"); +} + +TEST(EncodeQueryParamTest, TestUTF8Characters) { + string chineseCharacters = "中国語"; + string russianCharacters = "дом"; + string brazilianCharacters = "óculos"; + + EXPECT_EQ(detail::encode_query_param(chineseCharacters), + "%E4%B8%AD%E5%9B%BD%E8%AA%9E"); + + EXPECT_EQ(detail::encode_query_param(russianCharacters), + "%D0%B4%D0%BE%D0%BC"); + + EXPECT_EQ(detail::encode_query_param(brazilianCharacters), "%C3%B3culos"); +} + +TEST(TrimTests, TrimStringTests) { + EXPECT_EQ("abc", detail::trim_copy("abc")); + EXPECT_EQ("abc", detail::trim_copy(" abc ")); + EXPECT_TRUE(detail::trim_copy("").empty()); +} + +TEST(DivideTest, DivideStringTests) { + auto divide = [](const std::string &str, char d) { + std::string lhs; + std::string rhs; + + detail::divide(str, d, + [&](const char *lhs_data, std::size_t lhs_size, + const char *rhs_data, std::size_t rhs_size) { + lhs.assign(lhs_data, lhs_size); + rhs.assign(rhs_data, rhs_size); + }); + + return std::make_pair(std::move(lhs), std::move(rhs)); + }; + + { + const auto res = divide("", '='); + EXPECT_EQ(res.first, ""); + EXPECT_EQ(res.second, ""); + } + + { + const auto res = divide("=", '='); + EXPECT_EQ(res.first, ""); + EXPECT_EQ(res.second, ""); + } + + { + const auto res = divide(" ", '='); + EXPECT_EQ(res.first, " "); + EXPECT_EQ(res.second, ""); + } + + { + const auto res = divide("a", '='); + EXPECT_EQ(res.first, "a"); + EXPECT_EQ(res.second, ""); + } + + { + const auto res = divide("a=", '='); + EXPECT_EQ(res.first, "a"); + EXPECT_EQ(res.second, ""); + } + + { + const auto res = divide("=b", '='); + EXPECT_EQ(res.first, ""); + EXPECT_EQ(res.second, "b"); + } + + { + const auto res = divide("a=b", '='); + EXPECT_EQ(res.first, "a"); + EXPECT_EQ(res.second, "b"); + } + + { + const auto res = divide("a=b=", '='); + EXPECT_EQ(res.first, "a"); + EXPECT_EQ(res.second, "b="); + } + + { + const auto res = divide("a=b=c", '='); + EXPECT_EQ(res.first, "a"); + EXPECT_EQ(res.second, "b=c"); + } +} + +TEST(SplitTest, ParseQueryString) { + string s = "key1=val1&key2=val2&key3=val3"; + Params dic; + + detail::split(s.c_str(), s.c_str() + s.size(), '&', + [&](const char *b, const char *e) { + string key, val; + detail::split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + dic.emplace(key, val); + }); + + EXPECT_EQ("val1", dic.find("key1")->second); + EXPECT_EQ("val2", dic.find("key2")->second); + EXPECT_EQ("val3", dic.find("key3")->second); +} + +TEST(SplitTest, ParseInvalidQueryTests) { + + { + string s = " "; + Params dict; + detail::parse_query_text(s, dict); + EXPECT_TRUE(dict.empty()); + } + + { + string s = " = ="; + Params dict; + detail::parse_query_text(s, dict); + EXPECT_TRUE(dict.empty()); + } +} + +TEST(ParseQueryTest, ParseQueryString) { + { + std::string s = "key1=val1&key2=val2&key3=val3"; + Params dic; + + detail::parse_query_text(s, dic); + + EXPECT_EQ("val1", dic.find("key1")->second); + EXPECT_EQ("val2", dic.find("key2")->second); + EXPECT_EQ("val3", dic.find("key3")->second); + } + + { + std::string s = "key1&key2=val1&key3=val1=val2&key4=val1=val2=val3"; + Params dic; + + detail::parse_query_text(s, dic); + + EXPECT_EQ("", dic.find("key1")->second); + EXPECT_EQ("val1", dic.find("key2")->second); + EXPECT_EQ("val1=val2", dic.find("key3")->second); + EXPECT_EQ("val1=val2=val3", dic.find("key4")->second); + } +} + +TEST(ParamsToQueryTest, ConvertParamsToQuery) { + Params dic; + + EXPECT_EQ(detail::params_to_query_str(dic), ""); + + dic.emplace("key1", "val1"); + + EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1"); + + dic.emplace("key2", "val2"); + dic.emplace("key3", "val3"); + + EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1&key2=val2&key3=val3"); +} + +TEST(ParseMultipartBoundaryTest, DefaultValue) { + string content_type = "multipart/form-data; boundary=something"; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "something"); +} + +TEST(ParseMultipartBoundaryTest, ValueWithQuote) { + string content_type = "multipart/form-data; boundary=\"gc0pJq0M:08jU534c0p\""; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "gc0pJq0M:08jU534c0p"); +} + +TEST(ParseMultipartBoundaryTest, ValueWithCharset) { + string content_type = + "multipart/mixed; boundary=THIS_STRING_SEPARATES;charset=UTF-8"; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "THIS_STRING_SEPARATES"); +} + +TEST(ParseMultipartBoundaryTest, ValueWithQuotesAndCharset) { + string content_type = + "multipart/mixed; boundary=\"cpp-httplib-multipart-data\"; charset=UTF-8"; + string boundary; + auto ret = detail::parse_multipart_boundary(content_type, boundary); + EXPECT_TRUE(ret); + EXPECT_EQ(boundary, "cpp-httplib-multipart-data"); +} + +TEST(GetHeaderValueTest, DefaultValue) { + Headers headers = {{"Dummy", "Dummy"}}; + auto val = detail::get_header_value(headers, "Content-Type", 0, "text/plain"); + EXPECT_STREQ("text/plain", val); +} + +TEST(GetHeaderValueTest, DefaultValueInt) { + Headers headers = {{"Dummy", "Dummy"}}; + auto val = detail::get_header_value_u64(headers, "Content-Length", 0, 100); + EXPECT_EQ(100ull, val); +} + +TEST(GetHeaderValueTest, RegularValue) { + Headers headers = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}}; + auto val = detail::get_header_value(headers, "Content-Type", 0, "text/plain"); + EXPECT_STREQ("text/html", val); +} + +TEST(GetHeaderValueTest, RegularValueWithDifferentCase) { + Headers headers = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}}; + auto val = detail::get_header_value(headers, "content-type", 0, "text/plain"); + EXPECT_STREQ("text/html", val); +} + +TEST(GetHeaderValueTest, SetContent) { + Response res; + + res.set_content("html", "text/html"); + EXPECT_EQ("text/html", res.get_header_value("Content-Type")); + + res.set_content("text", "text/plain"); + EXPECT_EQ(1U, res.get_header_value_count("Content-Type")); + EXPECT_EQ("text/plain", res.get_header_value("Content-Type")); +} + +TEST(GetHeaderValueTest, RegularValueInt) { + Headers headers = {{"Content-Length", "100"}, {"Dummy", "Dummy"}}; + auto val = detail::get_header_value_u64(headers, "Content-Length", 0, 0); + EXPECT_EQ(100ull, val); +} + +TEST(GetHeaderValueTest, Range) { + { + Headers headers = {make_range_header({{1, -1}})}; + auto val = detail::get_header_value(headers, "Range", 0, 0); + EXPECT_STREQ("bytes=1-", val); + } + + { + Headers headers = {make_range_header({{-1, 1}})}; + auto val = detail::get_header_value(headers, "Range", 0, 0); + EXPECT_STREQ("bytes=-1", val); + } + + { + Headers headers = {make_range_header({{1, 10}})}; + auto val = detail::get_header_value(headers, "Range", 0, 0); + EXPECT_STREQ("bytes=1-10", val); + } + + { + Headers headers = {make_range_header({{1, 10}, {100, -1}})}; + auto val = detail::get_header_value(headers, "Range", 0, 0); + EXPECT_STREQ("bytes=1-10, 100-", val); + } + + { + Headers headers = {make_range_header({{1, 10}, {100, 200}})}; + auto val = detail::get_header_value(headers, "Range", 0, 0); + EXPECT_STREQ("bytes=1-10, 100-200", val); + } + + { + Headers headers = {make_range_header({{0, 0}, {-1, 1}})}; + auto val = detail::get_header_value(headers, "Range", 0, 0); + EXPECT_STREQ("bytes=0-0, -1", val); + } +} + +TEST(ParseHeaderValueTest, Range) { + { + Ranges ranges; + auto ret = detail::parse_range_header("bytes=1-", ranges); + EXPECT_TRUE(ret); + EXPECT_EQ(1u, ranges.size()); + EXPECT_EQ(1u, ranges[0].first); + EXPECT_EQ(-1, ranges[0].second); + } + + { + Ranges ranges; + auto ret = detail::parse_range_header("bytes=-1", ranges); + EXPECT_TRUE(ret); + EXPECT_EQ(1u, ranges.size()); + EXPECT_EQ(-1, ranges[0].first); + EXPECT_EQ(1u, ranges[0].second); + } + + { + Ranges ranges; + auto ret = detail::parse_range_header("bytes=1-10", ranges); + EXPECT_TRUE(ret); + EXPECT_EQ(1u, ranges.size()); + EXPECT_EQ(1u, ranges[0].first); + EXPECT_EQ(10u, ranges[0].second); + } + + { + Ranges ranges; + auto ret = detail::parse_range_header("bytes=10-1", ranges); + EXPECT_FALSE(ret); + } + + { + Ranges ranges; + auto ret = detail::parse_range_header("bytes=1-10, 100-", ranges); + EXPECT_TRUE(ret); + EXPECT_EQ(2u, ranges.size()); + EXPECT_EQ(1u, ranges[0].first); + EXPECT_EQ(10u, ranges[0].second); + EXPECT_EQ(100u, ranges[1].first); + EXPECT_EQ(-1, ranges[1].second); + } + + { + Ranges ranges; + auto ret = + detail::parse_range_header("bytes=1-10, 100-200, 300-400", ranges); + EXPECT_TRUE(ret); + EXPECT_EQ(3u, ranges.size()); + EXPECT_EQ(1u, ranges[0].first); + EXPECT_EQ(10u, ranges[0].second); + EXPECT_EQ(100u, ranges[1].first); + EXPECT_EQ(200u, ranges[1].second); + EXPECT_EQ(300u, ranges[2].first); + EXPECT_EQ(400u, ranges[2].second); + } + + { + Ranges ranges; + + EXPECT_FALSE(detail::parse_range_header("bytes", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=0", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=-", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes= ", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=,", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=,,", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=,,,", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=a-b", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=1-0", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=0--1", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=0- 1", ranges)); + EXPECT_FALSE(detail::parse_range_header("bytes=0 -1", ranges)); + EXPECT_TRUE(ranges.empty()); + } +} + +TEST(ParseAcceptEncoding1, AcceptEncoding) { + Request req; + req.set_header("Accept-Encoding", "gzip"); + + Response res; + res.set_header("Content-Type", "text/plain"); + + auto ret = detail::encoding_type(req, res); + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + EXPECT_TRUE(ret == detail::EncodingType::Gzip); +#else + EXPECT_TRUE(ret == detail::EncodingType::None); +#endif +} + +TEST(ParseAcceptEncoding2, AcceptEncoding) { + Request req; + req.set_header("Accept-Encoding", "gzip, deflate, br"); + + Response res; + res.set_header("Content-Type", "text/plain"); + + auto ret = detail::encoding_type(req, res); + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + EXPECT_TRUE(ret == detail::EncodingType::Brotli); +#elif CPPHTTPLIB_ZLIB_SUPPORT + EXPECT_TRUE(ret == detail::EncodingType::Gzip); +#else + EXPECT_TRUE(ret == detail::EncodingType::None); +#endif +} + +TEST(ParseAcceptEncoding3, AcceptEncoding) { + Request req; + req.set_header("Accept-Encoding", "br;q=1.0, gzip;q=0.8, *;q=0.1"); + + Response res; + res.set_header("Content-Type", "text/plain"); + + auto ret = detail::encoding_type(req, res); + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + EXPECT_TRUE(ret == detail::EncodingType::Brotli); +#elif CPPHTTPLIB_ZLIB_SUPPORT + EXPECT_TRUE(ret == detail::EncodingType::Gzip); +#else + EXPECT_TRUE(ret == detail::EncodingType::None); +#endif +} + +TEST(BufferStreamTest, read) { + detail::BufferStream strm1; + Stream &strm = strm1; + + EXPECT_EQ(5, strm.write("hello")); + + char buf[512]; + EXPECT_EQ(2, strm.read(buf, 2)); + EXPECT_EQ('h', buf[0]); + EXPECT_EQ('e', buf[1]); + + EXPECT_EQ(2, strm.read(buf, 2)); + EXPECT_EQ('l', buf[0]); + EXPECT_EQ('l', buf[1]); + + EXPECT_EQ(1, strm.read(buf, 1)); + EXPECT_EQ('o', buf[0]); + + EXPECT_EQ(0, strm.read(buf, 1)); +} + +TEST(ChunkedEncodingTest, FromHTTPWatch_Online) { + auto host = "www.httpwatch.com"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(2); + + auto res = + cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137"); + ASSERT_TRUE(res); + + std::string out; + detail::read_file("./image.jpg", out); + + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(out, res->body); +} + +TEST(HostnameToIPConversionTest, HTTPWatch_Online) { + auto host = "www.httpwatch.com"; + + auto ip = hosted_at(host); + EXPECT_EQ("23.96.13.243", ip); + + std::vector addrs; + hosted_at(host, addrs); + EXPECT_EQ(1u, addrs.size()); +} + +#if 0 // It depends on each test environment... +TEST(HostnameToIPConversionTest, YouTube_Online) { + auto host = "www.youtube.com"; + + std::vector addrs; + hosted_at(host, addrs); + + EXPECT_EQ(20u, addrs.size()); + + auto it = std::find(addrs.begin(), addrs.end(), "2607:f8b0:4006:809::200e"); + EXPECT_TRUE(it != addrs.end()); +} +#endif + +TEST(ChunkedEncodingTest, WithContentReceiver_Online) { + auto host = "www.httpwatch.com"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(2); + + std::string body; + auto res = + cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137", + [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + ASSERT_TRUE(res); + + std::string out; + detail::read_file("./image.jpg", out); + + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(out, body); +} + +TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver_Online) { + auto host = "www.httpwatch.com"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(2); + + std::string body; + auto res = cli.Get( + "/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137", + [&](const Response &response) { + EXPECT_EQ(StatusCode::OK_200, response.status); + return true; + }, + [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + ASSERT_TRUE(res); + + std::string out; + detail::read_file("./image.jpg", out); + + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(out, body); +} + +TEST(RangeTest, FromHTTPBin_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/range/32"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/range/32"}; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(5); + + { + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + Headers headers = {make_range_header({{1, -1}})}; + auto res = cli.Get(path, headers); + ASSERT_TRUE(res); + EXPECT_EQ("bcdefghijklmnopqrstuvwxyzabcdef", res->body); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + } + + { + Headers headers = {make_range_header({{1, 10}})}; + auto res = cli.Get(path, headers); + ASSERT_TRUE(res); + EXPECT_EQ("bcdefghijk", res->body); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + } + + { + Headers headers = {make_range_header({{0, 31}})}; + auto res = cli.Get(path, headers); + ASSERT_TRUE(res); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + Headers headers = {make_range_header({{0, -1}})}; + auto res = cli.Get(path, headers); + ASSERT_TRUE(res); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + Headers headers = {make_range_header({{0, 32}})}; + auto res = cli.Get(path, headers); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + } +} + +TEST(ConnectionErrorTest, InvalidHost) { + auto host = "-abcde.com"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(std::chrono::seconds(2)); + + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); +} + +TEST(ConnectionErrorTest, InvalidHost2) { + auto host = "httpbin.org/"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + cli.set_connection_timeout(std::chrono::seconds(2)); + + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); +} + +TEST(ConnectionErrorTest, InvalidHostCheckResultErrorToString) { + auto host = "httpbin.org/"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + cli.set_connection_timeout(std::chrono::seconds(2)); + + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + stringstream s; + s << "error code: " << res.error(); + EXPECT_EQ("error code: Could not establish connection (2)", s.str()); +} + +TEST(ConnectionErrorTest, InvalidPort) { + auto host = "localhost"; + auto port = 44380; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host, port); +#else + Client cli(host, port); +#endif + cli.set_connection_timeout(std::chrono::seconds(2)); + + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + EXPECT_TRUE(Error::Connection == res.error() || + Error::ConnectionTimeout == res.error()); +} + +TEST(ConnectionErrorTest, Timeout_Online) { + auto host = "google.com"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 44380; + SSLClient cli(host, port); +#else + auto port = 8080; + Client cli(host, port); +#endif + cli.set_connection_timeout(std::chrono::seconds(2)); + + // only probe one address type so that the error reason + // correlates to the timed-out IPv4, not the unsupported + // IPv6 connection attempt + cli.set_address_family(AF_INET); + + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::ConnectionTimeout, res.error()); +} + +TEST(CancelTest, NoCancel_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/range/32"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/range/32"}; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = cli.Get(path, [](uint64_t, uint64_t) { return true; }); + ASSERT_TRUE(res); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(CancelTest, WithCancelSmallPayload_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/range/32"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/range/32"}; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + + auto res = cli.Get(path, [](uint64_t, uint64_t) { return false; }); + cli.set_connection_timeout(std::chrono::seconds(5)); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, WithCancelLargePayload_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/range/65536"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/range/65536"}; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + cli.set_connection_timeout(std::chrono::seconds(5)); + + uint32_t count = 0; + auto res = + cli.Get(path, [&count](uint64_t, uint64_t) { return (count++ == 0); }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, NoCancelPost) { + Server svr; + + svr.Post("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Post("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return true; }); + ASSERT_TRUE(res); + EXPECT_EQ("Hello World!", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(CancelTest, WithCancelSmallPayloadPost) { + Server svr; + + svr.Post("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Post("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, WithCancelLargePayloadPost) { + Server svr; + + svr.Post("/", [&](const Request & /*req*/, Response &res) { + res.set_content(LARGE_DATA, "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Post("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, NoCancelPut) { + Server svr; + + svr.Put("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Put("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return true; }); + ASSERT_TRUE(res); + EXPECT_EQ("Hello World!", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(CancelTest, WithCancelSmallPayloadPut) { + Server svr; + + svr.Put("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Put("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, WithCancelLargePayloadPut) { + Server svr; + + svr.Put("/", [&](const Request & /*req*/, Response &res) { + res.set_content(LARGE_DATA, "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Put("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, NoCancelPatch) { + Server svr; + + svr.Patch("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Patch("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return true; }); + ASSERT_TRUE(res); + EXPECT_EQ("Hello World!", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(CancelTest, WithCancelSmallPayloadPatch) { + Server svr; + + svr.Patch("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Patch("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, WithCancelLargePayloadPatch) { + Server svr; + + svr.Patch("/", [&](const Request & /*req*/, Response &res) { + res.set_content(LARGE_DATA, "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Patch("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, NoCancelDelete) { + Server svr; + + svr.Delete("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Delete("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return true; }); + ASSERT_TRUE(res); + EXPECT_EQ("Hello World!", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(CancelTest, WithCancelSmallPayloadDelete) { + Server svr; + + svr.Delete("/", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Delete("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(CancelTest, WithCancelLargePayloadDelete) { + Server svr; + + svr.Delete("/", [&](const Request & /*req*/, Response &res) { + res.set_content(LARGE_DATA, "text/plain"); + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_connection_timeout(std::chrono::seconds(5)); + + auto res = + cli.Delete("/", Headers(), JSON_DATA.data(), JSON_DATA.size(), + "application/json", [](uint64_t, uint64_t) { return false; }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST(BaseAuthTest, FromHTTPWatch_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/basic-auth/hello/world"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/basic-auth/hello/world"}; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + SSLClient cli(host, port); +#else + auto port = 80; + Client cli(host, port); +#endif + + { + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + { + auto res = + cli.Get(path, {make_basic_authentication_header("hello", "world")}); + ASSERT_TRUE(res); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + cli.set_basic_auth("hello", "world"); + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + cli.set_basic_auth("hello", "bad"); + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + { + cli.set_basic_auth("bad", "world"); + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(DigestAuthTest, FromHTTPWatch_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto unauth_path = std::string{"/digest-auth/auth/hello/world"}; + auto paths = std::vector{ + "/digest-auth/auth/hello/world/MD5", + "/digest-auth/auth/hello/world/SHA-256", + "/digest-auth/auth/hello/world/SHA-512", + "/digest-auth/auth-int/hello/world/MD5", + }; +#else + auto host = "nghttp2.org"; + auto unauth_path = std::string{"/httpbin/digest-auth/auth/hello/world"}; + auto paths = std::vector{ + "/httpbin/digest-auth/auth/hello/world/MD5", + "/httpbin/digest-auth/auth/hello/world/SHA-256", + "/httpbin/digest-auth/auth/hello/world/SHA-512", + "/httpbin/digest-auth/auth-int/hello/world/MD5", + }; +#endif + + auto port = 443; + SSLClient cli(host, port); + + { + auto res = cli.Get(unauth_path); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + { + + cli.set_digest_auth("hello", "world"); + for (const auto &path : paths) { + auto res = cli.Get(path.c_str()); + ASSERT_TRUE(res); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + cli.set_digest_auth("hello", "bad"); + for (const auto &path : paths) { + auto res = cli.Get(path.c_str()); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + // NOTE: Until httpbin.org fixes issue #46, the following test is commented + // out. Please see https://httpbin.org/digest-auth/auth/hello/world + // cli.set_digest_auth("bad", "world"); + // for (const auto& path : paths) { + // auto res = cli.Get(path.c_str()); + // ASSERT_TRUE(res); + // EXPECT_EQ(StatusCode::BadRequest_400, res->status); + // } + } +} +#endif + +TEST(SpecifyServerIPAddressTest, AnotherHostname_Online) { + auto host = "google.com"; + auto another_host = "example.com"; + auto wrong_ip = "0.0.0.0"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_hostname_addr_map({{another_host, wrong_ip}}); + auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::MovedPermanently_301, res->status); +} + +TEST(SpecifyServerIPAddressTest, RealHostname_Online) { + auto host = "google.com"; + auto wrong_ip = "0.0.0.0"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_hostname_addr_map({{host, wrong_ip}}); + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); +} + +TEST(AbsoluteRedirectTest, Redirect_Online) { + auto host = "nghttp2.org"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_follow_location(true); + auto res = cli.Get("/httpbin/absolute-redirect/3"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(RedirectTest, Redirect_Online) { + auto host = "nghttp2.org"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_follow_location(true); + auto res = cli.Get("/httpbin/redirect/3"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(RelativeRedirectTest, Redirect_Online) { + auto host = "nghttp2.org"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_follow_location(true); + auto res = cli.Get("/httpbin/relative-redirect/3"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(TooManyRedirectTest, Redirect_Online) { + auto host = "nghttp2.org"; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_follow_location(true); + auto res = cli.Get("/httpbin/redirect/21"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::ExceedRedirectCount, res.error()); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(YahooRedirectTest, Redirect_Online) { + Client cli("yahoo.com"); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); + + cli.set_follow_location(true); + res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("https://www.yahoo.com/", res->location); +} + +TEST(HttpsToHttpRedirectTest, Redirect_Online) { + SSLClient cli("nghttp2.org"); + cli.set_follow_location(true); + auto res = cli.Get( + "/httpbin/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(HttpsToHttpRedirectTest2, Redirect_Online) { + SSLClient cli("nghttp2.org"); + cli.set_follow_location(true); + + Params params; + params.emplace("url", "http://www.google.com"); + params.emplace("status_code", "302"); + + auto res = cli.Get("/httpbin/redirect-to", params, Headers{}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(HttpsToHttpRedirectTest3, Redirect_Online) { + SSLClient cli("nghttp2.org"); + cli.set_follow_location(true); + + Params params; + params.emplace("url", "http://www.google.com"); + + auto res = cli.Get("/httpbin/redirect-to?status_code=302", params, Headers{}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(UrlWithSpace, Redirect_Online) { + SSLClient cli("edge.forgecdn.net"); + cli.set_follow_location(true); + + auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(18527U, res->get_header_value_u64("Content-Length")); +} + +#endif + +#if !defined(_WIN32) && !defined(_WIN64) +TEST(ReceiveSignals, Signal) { + auto setupSignalHandlers = []() { + struct sigaction act; + + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = [](int sig, siginfo_t *, void *) { + switch (sig) { + case SIGINT: + default: break; + } + }; + ::sigaction(SIGINT, &act, nullptr); + }; + + Server svr; + int port = 0; + auto thread = std::thread([&]() { + setupSignalHandlers(); + port = svr.bind_to_any_port("localhost"); + svr.listen_after_bind(); + }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + ASSERT_TRUE(svr.is_running()); + pthread_kill(thread.native_handle(), SIGINT); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + ASSERT_TRUE(svr.is_running()); +} +#endif + +TEST(RedirectToDifferentPort, Redirect) { + Server svr1; + svr1.Get("/1", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + int svr1_port = 0; + auto thread1 = std::thread([&]() { + svr1_port = svr1.bind_to_any_port("localhost"); + svr1.listen_after_bind(); + }); + + Server svr2; + svr2.Get("/2", [&](const Request & /*req*/, Response &res) { + res.set_redirect("http://localhost:" + std::to_string(svr1_port) + "/1"); + }); + + int svr2_port = 0; + auto thread2 = std::thread([&]() { + svr2_port = svr2.bind_to_any_port("localhost"); + svr2.listen_after_bind(); + }); + auto se = detail::scope_exit([&] { + svr2.stop(); + thread2.join(); + svr1.stop(); + thread1.join(); + ASSERT_FALSE(svr2.is_running()); + ASSERT_FALSE(svr1.is_running()); + }); + + svr1.wait_until_ready(); + svr2.wait_until_ready(); + + Client cli("localhost", svr2_port); + cli.set_follow_location(true); + + auto res = cli.Get("/2"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!", res->body); +} + +TEST(RedirectFromPageWithContent, Redirect) { + Server svr; + + svr.Get("/1", [&](const Request & /*req*/, Response &res) { + res.set_content("___", "text/plain"); + res.set_redirect("/2"); + }); + + svr.Get("/2", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + auto th = std::thread([&]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + th.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli("localhost", PORT); + cli.set_follow_location(true); + + std::string body; + auto res = cli.Get("/1", [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!", body); + } + + { + Client cli("localhost", PORT); + + std::string body; + auto res = cli.Get("/1", [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Found_302, res->status); + EXPECT_EQ("___", body); + } +} + +TEST(RedirectFromPageWithContentIP6, Redirect) { + Server svr; + + svr.Get("/1", [&](const Request & /*req*/, Response &res) { + res.set_content("___", "text/plain"); + // res.set_redirect("/2"); + res.set_redirect("http://[::1]:1234/2"); + }); + + svr.Get("/2", [&](const Request &req, Response &res) { + auto host_header = req.headers.find("Host"); + ASSERT_TRUE(host_header != req.headers.end()); + EXPECT_EQ("[::1]:1234", host_header->second); + + res.set_content("Hello World!", "text/plain"); + }); + + auto th = std::thread([&]() { svr.listen("::1", 1234); }); + auto se = detail::scope_exit([&] { + svr.stop(); + th.join(); + ASSERT_FALSE(svr.is_running()); + }); + + // When IPV6 support isn't available svr.listen("::1", 1234) never + // actually starts anything, so the condition !svr.is_running() will + // always remain true, and the loop never stops. + // This basically counts how many milliseconds have passed since the + // call to svr.listen(), and if after 5 seconds nothing started yet + // aborts the test. + for (unsigned int milliseconds = 0; !svr.is_running(); milliseconds++) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ASSERT_LT(milliseconds, 5000U); + } + + { + Client cli("http://[::1]:1234"); + cli.set_follow_location(true); + + std::string body; + auto res = cli.Get("/1", [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!", body); + } + + { + Client cli("http://[::1]:1234"); + + std::string body; + auto res = cli.Get("/1", [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Found_302, res->status); + EXPECT_EQ("___", body); + } +} + +TEST(PathUrlEncodeTest, PathUrlEncode) { + Server svr; + + svr.Get("/foo", [](const Request &req, Response &res) { + auto a = req.params.find("a"); + if (a != req.params.end()) { + res.set_content((*a).second, "text/plain"); + res.status = StatusCode::OK_200; + } else { + res.status = StatusCode::BadRequest_400; + } + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + cli.set_url_encode(false); + + auto res = cli.Get("/foo?a=explicitly+encoded"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + // This expects it back with a space, as the `+` won't have been + // url-encoded, and server-side the params get decoded turning `+` + // into spaces. + EXPECT_EQ("explicitly encoded", res->body); + } +} + +TEST(BindServerTest, DISABLED_BindDualStack) { + Server svr; + + svr.Get("/1", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + auto thread = std::thread([&]() { svr.listen("::", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli("127.0.0.1", PORT); + + auto res = cli.Get("/1"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!", res->body); + } + { + Client cli("::1", PORT); + + auto res = cli.Get("/1"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!", res->body); + } +} + +TEST(BindServerTest, BindAndListenSeparately) { + Server svr; + int port = svr.bind_to_any_port("0.0.0.0"); + ASSERT_TRUE(svr.is_valid()); + ASSERT_TRUE(port > 0); + svr.stop(); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(BindServerTest, BindAndListenSeparatelySSL) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, + CLIENT_CA_CERT_DIR); + int port = svr.bind_to_any_port("0.0.0.0"); + ASSERT_TRUE(svr.is_valid()); + ASSERT_TRUE(port > 0); + svr.stop(); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(BindServerTest, BindAndListenSeparatelySSLEncryptedKey) { + SSLServer svr(SERVER_ENCRYPTED_CERT_FILE, SERVER_ENCRYPTED_PRIVATE_KEY_FILE, + nullptr, nullptr, SERVER_ENCRYPTED_PRIVATE_KEY_PASS); + int port = svr.bind_to_any_port("0.0.0.0"); + ASSERT_TRUE(svr.is_valid()); + ASSERT_TRUE(port > 0); + svr.stop(); +} +#endif + +TEST(ErrorHandlerTest, ContentLength) { + Server svr; + + svr.set_error_handler([](const Request & /*req*/, Response &res) { + res.status = StatusCode::OK_200; + res.set_content("abcdefghijklmnopqrstuvwxyz", + "text/html"); // <= Content-Length still 13 + }); + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!\n", "text/plain"); + res.status = 524; + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("26", res->get_header_value("Content-Length")); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body); + } +} + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS +TEST(ExceptionHandlerTest, ContentLength) { + Server svr; + + svr.set_exception_handler([](const Request & /*req*/, Response &res, + std::exception_ptr ep) { + EXPECT_FALSE(ep == nullptr); + try { + std::rethrow_exception(ep); + } catch (std::exception &e) { EXPECT_EQ("abc", std::string(e.what())); } + res.status = StatusCode::InternalServerError_500; + res.set_content("abcdefghijklmnopqrstuvwxyz", + "text/html"); // <= Content-Length still 13 at this point + }); + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!\n", "text/plain"); + throw std::runtime_error("abc"); + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + for (size_t i = 0; i < 10; i++) { + Client cli(HOST, PORT); + + for (size_t j = 0; j < 100; j++) { + auto res = cli.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::InternalServerError_500, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("26", res->get_header_value("Content-Length")); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body); + } + + cli.set_keep_alive(true); + + for (size_t j = 0; j < 100; j++) { + auto res = cli.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::InternalServerError_500, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("26", res->get_header_value("Content-Length")); + EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body); + } + } +} +#endif + +TEST(NoContentTest, ContentLength) { + Server svr; + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.status = StatusCode::NoContent_204; + }); + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NoContent_204, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + } +} + +TEST(RoutingHandlerTest, PreRoutingHandler) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(svr.is_valid()); +#else + Server svr; +#endif + + svr.set_pre_routing_handler([](const Request &req, Response &res) { + if (req.path == "/routing_handler") { + res.set_header("PRE_ROUTING", "on"); + res.set_content("Routing Handler", "text/plain"); + return httplib::Server::HandlerResponse::Handled; + } + return httplib::Server::HandlerResponse::Unhandled; + }); + + svr.set_error_handler([](const Request & /*req*/, Response &res) { + res.set_content("Error", "text/html"); + }); + + svr.set_post_routing_handler([](const Request &req, Response &res) { + if (req.path == "/routing_handler") { + res.set_header("POST_ROUTING", "on"); + } + }); + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!\n", "text/plain"); + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(HOST, PORT); + cli.enable_server_certificate_verification(false); +#else + Client cli(HOST, PORT); +#endif + + auto res = cli.Get("/routing_handler"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Routing Handler", res->body); + EXPECT_EQ(1U, res->get_header_value_count("PRE_ROUTING")); + EXPECT_EQ("on", res->get_header_value("PRE_ROUTING")); + EXPECT_EQ(1U, res->get_header_value_count("POST_ROUTING")); + EXPECT_EQ("on", res->get_header_value("POST_ROUTING")); + } + + { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(HOST, PORT); + cli.enable_server_certificate_verification(false); +#else + Client cli(HOST, PORT); +#endif + + auto res = cli.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!\n", res->body); + EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING")); + EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING")); + } + + { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(HOST, PORT); + cli.enable_server_certificate_verification(false); +#else + Client cli(HOST, PORT); +#endif + + auto res = cli.Get("/aaa"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + EXPECT_EQ("Error", res->body); + EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING")); + EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING")); + } +} + +TEST(InvalidFormatTest, StatusCode) { + Server svr; + + svr.Get("/hi", [](const Request & /*req*/, Response &res) { + res.set_content("Hello World!\n", "text/plain"); + res.status = 9999; // Status should be a three-digit code... + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/hi"); + ASSERT_FALSE(res); + } +} + +TEST(URLFragmentTest, WithFragment) { + Server svr; + + svr.Get("/hi", [](const Request &req, Response & /*res*/) { + EXPECT_TRUE(req.target == "/hi"); + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/hi#key1=val1=key2=val2"); + EXPECT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + + res = cli.Get("/hi%23key1=val1=key2=val2"); + EXPECT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + } +} + +TEST(HeaderWriter, SetHeaderWriter) { + Server svr; + + svr.set_header_writer([](Stream &strm, Headers &hdrs) { + hdrs.emplace("CustomServerHeader", "CustomServerValue"); + return detail::write_headers(strm, hdrs); + }); + svr.Get("/hi", [](const Request &req, Response &res) { + auto it = req.headers.find("CustomClientHeader"); + EXPECT_TRUE(it != req.headers.end()); + EXPECT_EQ(it->second, "CustomClientValue"); + res.set_content("Hello World!\n", "text/plain"); + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + cli.set_header_writer([](Stream &strm, Headers &hdrs) { + hdrs.emplace("CustomClientHeader", "CustomClientValue"); + return detail::write_headers(strm, hdrs); + }); + + auto res = cli.Get("/hi"); + EXPECT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + + auto it = res->headers.find("CustomServerHeader"); + EXPECT_TRUE(it != res->headers.end()); + EXPECT_EQ(it->second, "CustomServerValue"); + } +} + +class ServerTest : public ::testing::Test { +protected: + ServerTest() + : cli_(HOST, PORT) +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + , + svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE) +#endif + { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_.enable_server_certificate_verification(false); +#endif + } + + virtual void SetUp() { + svr_.set_mount_point("/", "./www"); + svr_.set_mount_point("/mount", "./www2"); + svr_.set_file_extension_and_mimetype_mapping("abcde", "text/abcde"); + + svr_.Get("/hi", + [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }) + .Get("/http_response_splitting", + [&](const Request & /*req*/, Response &res) { + res.set_header("a", "1\r\nSet-Cookie: a=1"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("a")); + + res.set_header("a", "1\nSet-Cookie: a=1"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("a")); + + res.set_header("a", "1\rSet-Cookie: a=1"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("a")); + + res.set_header("a\r\nb", "0"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("a")); + + res.set_header("a\rb", "0"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("a")); + + res.set_header("a\nb", "0"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("a")); + + res.set_redirect("1\r\nSet-Cookie: a=1"); + EXPECT_EQ(0U, res.headers.size()); + EXPECT_FALSE(res.has_header("Location")); + }) + .Get("/slow", + [&](const Request & /*req*/, Response &res) { + std::this_thread::sleep_for(std::chrono::seconds(2)); + res.set_content("slow", "text/plain"); + }) +#if 0 + .Post("/slowpost", + [&](const Request & /*req*/, Response &res) { + std::this_thread::sleep_for(std::chrono::seconds(2)); + res.set_content("slow", "text/plain"); + }) +#endif + .Get("/remote_addr", + [&](const Request &req, Response &res) { + auto remote_addr = req.headers.find("REMOTE_ADDR")->second; + EXPECT_TRUE(req.has_header("REMOTE_PORT")); + EXPECT_EQ(req.remote_addr, req.get_header_value("REMOTE_ADDR")); + EXPECT_EQ(req.remote_port, + std::stoi(req.get_header_value("REMOTE_PORT"))); + res.set_content(remote_addr.c_str(), "text/plain"); + }) + .Get("/local_addr", + [&](const Request &req, Response &res) { + EXPECT_TRUE(req.has_header("LOCAL_PORT")); + EXPECT_TRUE(req.has_header("LOCAL_ADDR")); + auto local_addr = req.get_header_value("LOCAL_ADDR"); + auto local_port = req.get_header_value("LOCAL_PORT"); + EXPECT_EQ(req.local_addr, local_addr); + EXPECT_EQ(req.local_port, std::stoi(local_port)); + res.set_content(local_addr.append(":").append(local_port), + "text/plain"); + }) + .Get("/endwith%", + [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }) + .Get("/a\\+\\+b", + [&](const Request &req, Response &res) { + ASSERT_TRUE(req.has_param("a +b")); + auto val = req.get_param_value("a +b"); + res.set_content(val, "text/plain"); + }) + .Get("/", [&](const Request & /*req*/, + Response &res) { res.set_redirect("/hi"); }) + .Post("/1", + [](const Request & /*req*/, Response &res) { + res.set_redirect("/2", StatusCode::SeeOther_303); + }) + .Get("/2", + [](const Request & /*req*/, Response &res) { + res.set_content("redirected.", "text/plain"); + res.status = StatusCode::OK_200; + }) + .Post("/person", + [&](const Request &req, Response &res) { + if (req.has_param("name") && req.has_param("note")) { + persons_[req.get_param_value("name")] = + req.get_param_value("note"); + } else { + res.status = StatusCode::BadRequest_400; + } + }) + .Put("/person", + [&](const Request &req, Response &res) { + if (req.has_param("name") && req.has_param("note")) { + persons_[req.get_param_value("name")] = + req.get_param_value("note"); + } else { + res.status = StatusCode::BadRequest_400; + } + }) + .Get("/person/(.*)", + [&](const Request &req, Response &res) { + string name = req.matches[1]; + if (persons_.find(name) != persons_.end()) { + auto note = persons_[name]; + res.set_content(note, "text/plain"); + } else { + res.status = StatusCode::NotFound_404; + } + }) + .Post("/x-www-form-urlencoded-json", + [&](const Request &req, Response &res) { + auto json = req.get_param_value("json"); + ASSERT_EQ(JSON_DATA, json); + res.set_content(json, "appliation/json"); + res.status = StatusCode::OK_200; + }) + .Get("/streamed-chunked", + [&](const Request & /*req*/, Response &res) { + res.set_chunked_content_provider( + "text/plain", [](size_t /*offset*/, DataSink &sink) { + sink.os << "123"; + sink.os << "456"; + sink.os << "789"; + sink.done(); + return true; + }); + }) + .Get("/streamed-chunked2", + [&](const Request & /*req*/, Response &res) { + auto i = new int(0); + res.set_chunked_content_provider( + "text/plain", + [i](size_t /*offset*/, DataSink &sink) { + switch (*i) { + case 0: sink.os << "123"; break; + case 1: sink.os << "456"; break; + case 2: sink.os << "789"; break; + case 3: sink.done(); break; + } + (*i)++; + return true; + }, + [i](bool success) { + EXPECT_TRUE(success); + delete i; + }); + }) + .Get("/streamed-chunked-with-trailer", + [&](const Request & /*req*/, Response &res) { + auto i = new int(0); + res.set_header("Trailer", "Dummy1, Dummy2"); + res.set_chunked_content_provider( + "text/plain", + [i](size_t /*offset*/, DataSink &sink) { + switch (*i) { + case 0: sink.os << "123"; break; + case 1: sink.os << "456"; break; + case 2: sink.os << "789"; break; + case 3: { + sink.done_with_trailer( + {{"Dummy1", "DummyVal1"}, {"Dummy2", "DummyVal2"}}); + } break; + } + (*i)++; + return true; + }, + [i](bool success) { + EXPECT_TRUE(success); + delete i; + }); + }) + .Get("/streamed", + [&](const Request & /*req*/, Response &res) { + res.set_content_provider( + 6, "text/plain", + [](size_t offset, size_t /*length*/, DataSink &sink) { + sink.os << (offset < 3 ? "a" : "b"); + return true; + }); + }) + .Get("/streamed-with-range", + [&](const Request &req, Response &res) { + auto data = new std::string("abcdefg"); + res.set_content_provider( + data->size(), "text/plain", + [data](size_t offset, size_t length, DataSink &sink) { + size_t DATA_CHUNK_SIZE = 4; + const auto &d = *data; + auto out_len = + std::min(static_cast(length), DATA_CHUNK_SIZE); + auto ret = + sink.write(&d[static_cast(offset)], out_len); + EXPECT_TRUE(ret); + return true; + }, + [data, &req](bool success) { + EXPECT_EQ(success, !req.has_param("error")); + delete data; + }); + }) + .Get("/streamed-cancel", + [&](const Request & /*req*/, Response &res) { + res.set_content_provider( + size_t(-1), "text/plain", + [](size_t /*offset*/, size_t /*length*/, DataSink &sink) { + sink.os << "data_chunk"; + return true; + }); + }) + .Get("/regex-with-delimiter", + [&](const Request &req, Response & /*res*/) { + ASSERT_TRUE(req.has_param("key")); + EXPECT_EQ("^(?.*(value))", req.get_param_value("key")); + }) + .Get("/with-range", + [&](const Request & /*req*/, Response &res) { + res.set_content("abcdefg", "text/plain"); + }) + .Get("/with-range-customized-response", + [&](const Request & /*req*/, Response &res) { + res.status = StatusCode::BadRequest_400; + res.set_content(JSON_DATA, "application/json"); + }) + .Post("/chunked", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(req.body, "dechunked post body"); + }) + .Post("/large-chunked", + [&](const Request &req, Response & /*res*/) { + std::string expected(6 * 30 * 1024u, 'a'); + EXPECT_EQ(req.body, expected); + }) + .Post("/multipart", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(6u, req.files.size()); + ASSERT_TRUE(!req.has_file("???")); + ASSERT_TRUE(req.body.empty()); + + { + const auto &file = req.get_file_value("text1"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("text default", file.content); + } + + { + const auto &file = req.get_file_value("text2"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("aωb", file.content); + } + + { + const auto &file = req.get_file_value("file1"); + EXPECT_EQ("hello.txt", file.filename); + EXPECT_EQ("text/plain", file.content_type); + EXPECT_EQ("h\ne\n\nl\nl\no\n", file.content); + } + + { + const auto &file = req.get_file_value("file3"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("application/octet-stream", file.content_type); + EXPECT_EQ(0u, file.content.size()); + } + + { + const auto &file = req.get_file_value("file4"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ(0u, file.content.size()); + EXPECT_EQ("application/json tmp-string", file.content_type); + } + }) + .Post("/multipart/multi_file_values", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(5u, req.files.size()); + ASSERT_TRUE(!req.has_file("???")); + ASSERT_TRUE(req.body.empty()); + + { + const auto &text_value = req.get_file_values("text"); + EXPECT_EQ(1u, text_value.size()); + auto &text = text_value[0]; + EXPECT_TRUE(text.filename.empty()); + EXPECT_EQ("default text", text.content); + } + { + const auto &text1_values = req.get_file_values("multi_text1"); + EXPECT_EQ(2u, text1_values.size()); + EXPECT_EQ("aaaaa", text1_values[0].content); + EXPECT_EQ("bbbbb", text1_values[1].content); + } + + { + const auto &file1_values = req.get_file_values("multi_file1"); + EXPECT_EQ(2u, file1_values.size()); + auto file1 = file1_values[0]; + EXPECT_EQ(file1.filename, "hello.txt"); + EXPECT_EQ(file1.content_type, "text/plain"); + EXPECT_EQ("h\ne\n\nl\nl\no\n", file1.content); + + auto file2 = file1_values[1]; + EXPECT_EQ(file2.filename, "world.json"); + EXPECT_EQ(file2.content_type, "application/json"); + EXPECT_EQ("{\n \"world\", true\n}\n", file2.content); + } + }) + .Post("/empty", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, ""); + EXPECT_EQ("text/plain", req.get_header_value("Content-Type")); + EXPECT_EQ("0", req.get_header_value("Content-Length")); + res.set_content("empty", "text/plain"); + }) + .Post("/empty-no-content-type", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, ""); + EXPECT_FALSE(req.has_header("Content-Type")); + EXPECT_EQ("0", req.get_header_value("Content-Length")); + res.set_content("empty-no-content-type", "text/plain"); + }) + .Post("/path-only", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, ""); + EXPECT_EQ("", req.get_header_value("Content-Type")); + EXPECT_EQ("0", req.get_header_value("Content-Length")); + res.set_content("path-only", "text/plain"); + }) + .Post("/path-headers-only", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, ""); + EXPECT_EQ("", req.get_header_value("Content-Type")); + EXPECT_EQ("0", req.get_header_value("Content-Length")); + EXPECT_EQ("world", req.get_header_value("hello")); + EXPECT_EQ("world2", req.get_header_value("hello2")); + res.set_content("path-headers-only", "text/plain"); + }) + .Post("/post-large", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, LARGE_DATA); + res.set_content(req.body, "text/plain"); + }) + .Put("/empty-no-content-type", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, ""); + EXPECT_FALSE(req.has_header("Content-Type")); + EXPECT_EQ("0", req.get_header_value("Content-Length")); + res.set_content("empty-no-content-type", "text/plain"); + }) + .Put("/put", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, "PUT"); + res.set_content(req.body, "text/plain"); + }) + .Put("/put-large", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, LARGE_DATA); + res.set_content(req.body, "text/plain"); + }) + .Patch("/patch", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, "PATCH"); + res.set_content(req.body, "text/plain"); + }) + .Delete("/delete", + [&](const Request & /*req*/, Response &res) { + res.set_content("DELETE", "text/plain"); + }) + .Delete("/delete-body", + [&](const Request &req, Response &res) { + EXPECT_EQ(req.body, "content"); + res.set_content(req.body, "text/plain"); + }) + .Options(R"(\*)", + [&](const Request & /*req*/, Response &res) { + res.set_header("Allow", "GET, POST, HEAD, OPTIONS"); + }) + .Get("/request-target", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ("/request-target?aaa=bbb&ccc=ddd", req.target); + EXPECT_EQ("bbb", req.get_param_value("aaa")); + EXPECT_EQ("ddd", req.get_param_value("ccc")); + }) + .Get("/long-query-value", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(LONG_QUERY_URL, req.target); + EXPECT_EQ(LONG_QUERY_VALUE, req.get_param_value("key")); + }) + .Get("/array-param", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(3u, req.get_param_value_count("array")); + EXPECT_EQ("value1", req.get_param_value("array", 0)); + EXPECT_EQ("value2", req.get_param_value("array", 1)); + EXPECT_EQ("value3", req.get_param_value("array", 2)); + }) + .Post("/validate-no-multiple-headers", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(1u, req.get_header_value_count("Content-Length")); + EXPECT_EQ("5", req.get_header_value("Content-Length")); + }) + .Post("/content_receiver", + [&](const Request &req, Response &res, + const ContentReader &content_reader) { + if (req.is_multipart_form_data()) { + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + EXPECT_EQ(5u, files.size()); + + { + const auto &file = get_file_value(files, "text1"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("text default", file.content); + } + + { + const auto &file = get_file_value(files, "text2"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("aωb", file.content); + } + + { + const auto &file = get_file_value(files, "file1"); + EXPECT_EQ("hello.txt", file.filename); + EXPECT_EQ("text/plain", file.content_type); + EXPECT_EQ("h\ne\n\nl\nl\no\n", file.content); + } + + { + const auto &file = get_file_value(files, "file3"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("application/octet-stream", file.content_type); + EXPECT_EQ(0u, file.content.size()); + } + } else { + std::string body; + content_reader([&](const char *data, size_t data_length) { + EXPECT_EQ(7U, data_length); + body.append(data, data_length); + return true; + }); + EXPECT_EQ(body, "content"); + res.set_content(body, "text/plain"); + } + }) + .Put("/content_receiver", + [&](const Request & /*req*/, Response &res, + const ContentReader &content_reader) { + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + EXPECT_EQ(body, "content"); + res.set_content(body, "text/plain"); + }) + .Patch("/content_receiver", + [&](const Request & /*req*/, Response &res, + const ContentReader &content_reader) { + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + EXPECT_EQ(body, "content"); + res.set_content(body, "text/plain"); + }) + .Post("/query-string-and-body", + [&](const Request &req, Response & /*res*/) { + ASSERT_TRUE(req.has_param("key")); + EXPECT_EQ(req.get_param_value("key"), "value"); + EXPECT_EQ(req.body, "content"); + }) + .Get("/last-request", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ("close", req.get_header_value("Connection")); + }) + .Get(R"(/redirect/(\d+))", + [&](const Request &req, Response &res) { + auto num = std::stoi(req.matches[1]) + 1; + std::string url = "/redirect/" + std::to_string(num); + res.set_redirect(url); + }) + .Post("/binary", + [&](const Request &req, Response &res) { + EXPECT_EQ(4U, req.body.size()); + EXPECT_EQ("application/octet-stream", + req.get_header_value("Content-Type")); + EXPECT_EQ("4", req.get_header_value("Content-Length")); + res.set_content(req.body, "application/octet-stream"); + }) + .Put("/binary", + [&](const Request &req, Response &res) { + EXPECT_EQ(4U, req.body.size()); + EXPECT_EQ("application/octet-stream", + req.get_header_value("Content-Type")); + EXPECT_EQ("4", req.get_header_value("Content-Length")); + res.set_content(req.body, "application/octet-stream"); + }) + .Patch("/binary", + [&](const Request &req, Response &res) { + EXPECT_EQ(4U, req.body.size()); + EXPECT_EQ("application/octet-stream", + req.get_header_value("Content-Type")); + EXPECT_EQ("4", req.get_header_value("Content-Length")); + res.set_content(req.body, "application/octet-stream"); + }) + .Delete("/binary", + [&](const Request &req, Response &res) { + EXPECT_EQ(4U, req.body.size()); + EXPECT_EQ("application/octet-stream", + req.get_header_value("Content-Type")); + EXPECT_EQ("4", req.get_header_value("Content-Length")); + res.set_content(req.body, "application/octet-stream"); + }) + .Get("/issue1772", + [&](const Request & /*req*/, Response &res) { + res.status = 401; + res.set_header("WWW-Authenticate", "Basic realm=123456"); + }) +#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT) + .Get("/compress", + [&](const Request & /*req*/, Response &res) { + res.set_content( + "12345678901234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890", + "text/plain"); + }) + .Get("/nocompress", + [&](const Request & /*req*/, Response &res) { + res.set_content( + "12345678901234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890", + "application/octet-stream"); + }) + .Post("/compress-multipart", + [&](const Request &req, Response & /*res*/) { + EXPECT_EQ(2u, req.files.size()); + ASSERT_TRUE(!req.has_file("???")); + + { + const auto &file = req.get_file_value("key1"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("test", file.content); + } + + { + const auto &file = req.get_file_value("key2"); + EXPECT_TRUE(file.filename.empty()); + EXPECT_EQ("--abcdefg123", file.content); + } + }) +#endif + ; + + persons_["john"] = "programmer"; + + t_ = thread([&]() { ASSERT_TRUE(svr_.listen(HOST, PORT)); }); + + svr_.wait_until_ready(); + } + + virtual void TearDown() { + svr_.stop(); + if (!request_threads_.empty()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + for (auto &t : request_threads_) { + t.join(); + } + } + t_.join(); + } + + map persons_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli_; + SSLServer svr_; +#else + Client cli_; + Server svr_; +#endif + thread t_; + std::vector request_threads_; +}; + +TEST_F(ServerTest, GetMethod200) { + auto res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ("HTTP/1.1", res->version); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("OK", res->reason); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ(1U, res->get_header_value_count("Content-Type")); + EXPECT_EQ("Hello World!", res->body); +} + +TEST_F(ServerTest, GetMethod200withPercentEncoding) { + auto res = cli_.Get("/%68%69"); // auto res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ("HTTP/1.1", res->version); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ(1U, res->get_header_value_count("Content-Type")); + EXPECT_EQ("Hello World!", res->body); +} + +TEST_F(ServerTest, GetMethod302) { + auto res = cli_.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Found_302, res->status); + EXPECT_EQ("/hi", res->get_header_value("Location")); +} + +TEST_F(ServerTest, GetMethod302Redirect) { + cli_.set_follow_location(true); + auto res = cli_.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("Hello World!", res->body); + EXPECT_EQ("/hi", res->location); +} + +TEST_F(ServerTest, GetMethod404) { + auto res = cli_.Get("/invalid"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, HeadMethod200) { + auto res = cli_.Head("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_TRUE(res->body.empty()); +} + +TEST_F(ServerTest, HeadMethod200Static) { + auto res = cli_.Head("/mount/dir/index.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ(104, std::stoi(res->get_header_value("Content-Length"))); + EXPECT_TRUE(res->body.empty()); +} + +TEST_F(ServerTest, HeadMethod404) { + auto res = cli_.Head("/invalid"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + EXPECT_TRUE(res->body.empty()); +} + +TEST_F(ServerTest, GetMethodPersonJohn) { + auto res = cli_.Get("/person/john"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("programmer", res->body); +} + +TEST_F(ServerTest, PostMethod1) { + auto res = cli_.Get("/person/john1"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::NotFound_404, res->status); + + res = cli_.Post("/person", "name=john1¬e=coder", + "application/x-www-form-urlencoded"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + + res = cli_.Get("/person/john1"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); + ASSERT_EQ("coder", res->body); +} + +TEST_F(ServerTest, PostMethod2) { + auto res = cli_.Get("/person/john2"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::NotFound_404, res->status); + + Params params; + params.emplace("name", "john2"); + params.emplace("note", "coder"); + + res = cli_.Post("/person", params); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + + res = cli_.Get("/person/john2"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); + ASSERT_EQ("coder", res->body); +} + +TEST_F(ServerTest, PutMethod3) { + auto res = cli_.Get("/person/john3"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::NotFound_404, res->status); + + Params params; + params.emplace("name", "john3"); + params.emplace("note", "coder"); + + res = cli_.Put("/person", params); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + + res = cli_.Get("/person/john3"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); + ASSERT_EQ("coder", res->body); +} + +TEST_F(ServerTest, PostWwwFormUrlEncodedJson) { + Params params; + params.emplace("json", JSON_DATA); + + auto res = cli_.Post("/x-www-form-urlencoded-json", params); + + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(JSON_DATA, res->body); +} + +TEST_F(ServerTest, PostEmptyContent) { + auto res = cli_.Post("/empty", "", "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("empty", res->body); +} + +TEST_F(ServerTest, PostEmptyContentWithNoContentType) { + auto res = cli_.Post("/empty-no-content-type"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("empty-no-content-type", res->body); +} + +TEST_F(ServerTest, PostPathOnly) { + auto res = cli_.Post("/path-only"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("path-only", res->body); +} + +TEST_F(ServerTest, PostPathAndHeadersOnly) { + auto res = cli_.Post("/path-headers-only", + Headers({{"hello", "world"}, {"hello2", "world2"}})); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("path-headers-only", res->body); +} + +TEST_F(ServerTest, PostLarge) { + auto res = cli_.Post("/post-large", LARGE_DATA, "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(LARGE_DATA, res->body); +} + +TEST_F(ServerTest, PutEmptyContentWithNoContentType) { + auto res = cli_.Put("/empty-no-content-type"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("empty-no-content-type", res->body); +} + +TEST_F(ServerTest, GetMethodDir) { + auto res = cli_.Get("/dir/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + + auto body = R"( + + + + Test + hi + + +)"; + EXPECT_EQ(body, res->body); +} + +TEST_F(ServerTest, GetMethodDirTest) { + auto res = cli_.Get("/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("test.html", res->body); +} + +TEST_F(ServerTest, GetMethodDirTestWithDoubleDots) { + auto res = cli_.Get("/dir/../dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("test.html", res->body); +} + +TEST_F(ServerTest, GetMethodInvalidPath) { + auto res = cli_.Get("/dir/../test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodOutOfBaseDir) { + auto res = cli_.Get("/../www/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodOutOfBaseDir2) { + auto res = cli_.Get("/dir/../../www/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodDirMountTest) { + auto res = cli_.Get("/mount/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("test.html", res->body); +} + +TEST_F(ServerTest, GetMethodDirMountTestWithDoubleDots) { + auto res = cli_.Get("/mount/dir/../dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("test.html", res->body); +} + +TEST_F(ServerTest, GetMethodInvalidMountPath) { + auto res = cli_.Get("/mount/dir/../test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodEmbeddedNUL) { + auto res = cli_.Get("/mount/dir/test.html%00.js"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodOutOfBaseDirMount) { + auto res = cli_.Get("/mount/../www2/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) { + auto res = cli_.Get("/mount/dir/../../www2/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, GetMethodOutOfBaseDirMountWithBackslash) { + auto res = cli_.Get("/mount/%2e%2e%5c/www2/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, PostMethod303) { + auto res = cli_.Post("/1", "body", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::SeeOther_303, res->status); + EXPECT_EQ("/2", res->get_header_value("Location")); +} + +TEST_F(ServerTest, PostMethod303Redirect) { + cli_.set_follow_location(true); + auto res = cli_.Post("/1", "body", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("redirected.", res->body); + EXPECT_EQ("/2", res->location); +} + +TEST_F(ServerTest, UserDefinedMIMETypeMapping) { + auto res = cli_.Get("/dir/test.abcde"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/abcde", res->get_header_value("Content-Type")); + EXPECT_EQ("abcde", res->body); +} + +TEST_F(ServerTest, StaticFileRange) { + auto res = cli_.Get("/dir/test.abcde", {{make_range_header({{2, 3}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("text/abcde", res->get_header_value("Content-Type")); + EXPECT_EQ("2", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 2-3/5", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("cd"), res->body); +} + +TEST_F(ServerTest, StaticFileRanges) { + auto res = + cli_.Get("/dir/test.abcde", {{make_range_header({{1, 2}, {4, -1}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_TRUE( + res->get_header_value("Content-Type") + .find( + "multipart/byteranges; boundary=--cpp-httplib-multipart-data-") == + 0); + EXPECT_EQ("266", res->get_header_value("Content-Length")); +} + +TEST_F(ServerTest, StaticFileRangeHead) { + auto res = cli_.Head("/dir/test.abcde", {{make_range_header({{2, 3}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("text/abcde", res->get_header_value("Content-Type")); + EXPECT_EQ("2", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 2-3/5", res->get_header_value("Content-Range")); +} + +TEST_F(ServerTest, StaticFileRangeBigFile) { + auto res = cli_.Get("/dir/1MB.txt", {{make_range_header({{-1, 5}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("5", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 1048571-1048575/1048576", + res->get_header_value("Content-Range")); + EXPECT_EQ("LAST\n", res->body); +} + +TEST_F(ServerTest, StaticFileRangeBigFile2) { + auto res = cli_.Get("/dir/1MB.txt", {{make_range_header({{1, 4097}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("4097", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 1-4097/1048576", res->get_header_value("Content-Range")); +} + +TEST_F(ServerTest, StaticFileBigFile) { + auto res = cli_.Get("/dir/1MB.txt"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("1048576", res->get_header_value("Content-Length")); +} + +TEST_F(ServerTest, InvalidBaseDirMount) { + EXPECT_EQ(false, svr_.set_mount_point("invalid_mount_point", "./www3")); +} + +TEST_F(ServerTest, Binary) { + std::vector binary{0x00, 0x01, 0x02, 0x03}; + + auto res = cli_.Post("/binary", binary.data(), binary.size(), + "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); + + res = cli_.Put("/binary", binary.data(), binary.size(), + "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); + + res = cli_.Patch("/binary", binary.data(), binary.size(), + "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); + + res = cli_.Delete("/binary", binary.data(), binary.size(), + "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); +} + +TEST_F(ServerTest, BinaryString) { + auto binary = std::string("\x00\x01\x02\x03", 4); + + auto res = cli_.Post("/binary", binary, "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); + + res = cli_.Put("/binary", binary, "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); + + res = cli_.Patch("/binary", binary, "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); + + res = cli_.Delete("/binary", binary, "application/octet-stream"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ(4U, res->body.size()); +} + +TEST_F(ServerTest, EmptyRequest) { + auto res = cli_.Get(""); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); +} + +TEST_F(ServerTest, LongRequest) { + std::string request; + for (size_t i = 0; i < 545; i++) { + request += "/TooLongRequest"; + } + request += "OK"; + + auto res = cli_.Get(request.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, TooLongRequest) { + std::string request; + for (size_t i = 0; i < 545; i++) { + request += "/TooLongRequest"; + } + request += "_NG"; + + auto res = cli_.Get(request.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::UriTooLong_414, res->status); +} + +TEST_F(ServerTest, LongHeader) { + Request req; + req.method = "GET"; + req.path = "/hi"; + + std::string host_and_port; + host_and_port += HOST; + host_and_port += ":"; + host_and_port += std::to_string(PORT); + + req.headers.emplace("Host", host_and_port.c_str()); + req.headers.emplace("Accept", "*/*"); + req.headers.emplace("User-Agent", "cpp-httplib/0.1"); + + req.headers.emplace( + "Header-Name", + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@"); + + auto res = std::make_shared(); + auto error = Error::Success; + auto ret = cli_.send(req, *res, error); + + ASSERT_TRUE(ret); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, LongQueryValue) { + auto res = cli_.Get(LONG_QUERY_URL.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::UriTooLong_414, res->status); +} + +TEST_F(ServerTest, TooLongHeader) { + Request req; + req.method = "GET"; + req.path = "/hi"; + + std::string host_and_port; + host_and_port += HOST; + host_and_port += ":"; + host_and_port += std::to_string(PORT); + + req.headers.emplace("Host", host_and_port.c_str()); + req.headers.emplace("Accept", "*/*"); + req.headers.emplace("User-Agent", "cpp-httplib/0.1"); + + req.headers.emplace( + "Header-Name", + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + "@@@@@@@@@@@@@@@@@"); + + auto res = std::make_shared(); + auto error = Error::Success; + auto ret = cli_.send(req, *res, error); + + ASSERT_TRUE(ret); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, PercentEncoding) { + auto res = cli_.Get("/e%6edwith%"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, PercentEncodingUnicode) { + auto res = cli_.Get("/e%u006edwith%"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, InvalidPercentEncoding) { + auto res = cli_.Get("/%endwith%"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, InvalidPercentEncodingUnicode) { + auto res = cli_.Get("/%uendwith%"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, EndWithPercentCharacterInQuery) { + auto res = cli_.Get("/hello?aaa=bbb%"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +TEST_F(ServerTest, PlusSignEncoding) { + auto res = cli_.Get("/a+%2Bb?a %2bb=a %2Bb"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("a +b", res->body); +} + +TEST_F(ServerTest, MultipartFormData) { + MultipartFormDataItems items = { + {"text1", "text default", "", ""}, + {"text2", "aωb", "", ""}, + {"file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain"}, + {"file2", "{\n \"world\", true\n}\n", "world.json", "application/json"}, + {"file3", "", "", "application/octet-stream"}, + {"file4", "", "", " application/json tmp-string "}}; + + auto res = cli_.Post("/multipart", items); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, MultipartFormDataMultiFileValues) { + MultipartFormDataItems items = { + {"text", "default text", "", ""}, + + {"multi_text1", "aaaaa", "", ""}, + {"multi_text1", "bbbbb", "", ""}, + + {"multi_file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain"}, + {"multi_file1", "{\n \"world\", true\n}\n", "world.json", + "application/json"}, + }; + + auto res = cli_.Post("/multipart/multi_file_values", items); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, CaseInsensitiveHeaderName) { + auto res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("content-type")); + EXPECT_EQ("Hello World!", res->body); +} + +TEST_F(ServerTest, CaseInsensitiveTransferEncoding) { + Request req; + req.method = "POST"; + req.path = "/chunked"; + + std::string host_and_port; + host_and_port += HOST; + host_and_port += ":"; + host_and_port += std::to_string(PORT); + + req.headers.emplace("Host", host_and_port.c_str()); + req.headers.emplace("Accept", "*/*"); + req.headers.emplace("User-Agent", "cpp-httplib/0.1"); + req.headers.emplace("Content-Type", "text/plain"); + req.headers.emplace("Content-Length", "0"); + req.headers.emplace( + "Transfer-Encoding", + "Chunked"); // Note, "Chunked" rather than typical "chunked". + + // Client does not chunk, so make a chunked body manually. + req.body = "4\r\ndech\r\nf\r\nunked post body\r\n0\r\n\r\n"; + + auto res = std::make_shared(); + auto error = Error::Success; + auto ret = cli_.send(req, *res, error); + + ASSERT_TRUE(ret); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, GetStreamed2) { + auto res = cli_.Get("/streamed", {{make_range_header({{2, 3}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("2", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 2-3/6", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("ab"), res->body); +} + +TEST_F(ServerTest, GetStreamed) { + auto res = cli_.Get("/streamed"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("6", res->get_header_value("Content-Length")); + EXPECT_EQ(std::string("aaabbb"), res->body); +} + +TEST_F(ServerTest, GetStreamedWithRange1) { + auto res = cli_.Get("/streamed-with-range", {{make_range_header({{3, 5}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("3", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 3-5/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("def"), res->body); +} + +TEST_F(ServerTest, GetStreamedWithRange2) { + auto res = cli_.Get("/streamed-with-range", {{make_range_header({{1, -1}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("6", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 1-6/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("bcdefg"), res->body); +} + +TEST_F(ServerTest, GetStreamedWithRangeSuffix1) { + auto res = cli_.Get("/streamed-with-range", {{"Range", "bytes=-3"}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("3", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 4-6/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("efg"), res->body); +} + +TEST_F(ServerTest, GetStreamedWithRangeSuffix2) { + auto res = cli_.Get("/streamed-with-range?error", {{"Range", "bytes=-9999"}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(0U, res->body.size()); +} + +TEST_F(ServerTest, GetStreamedWithRangeError) { + auto res = cli_.Get("/streamed-with-range", + {{"Range", "bytes=92233720368547758079223372036854775806-" + "92233720368547758079223372036854775807"}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(0U, res->body.size()); +} + +TEST_F(ServerTest, GetRangeWithMaxLongLength) { + auto res = + cli_.Get("/with-range", {{"Range", "bytes=0-9223372036854775807"}}); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(0U, res->body.size()); +} + +TEST_F(ServerTest, GetStreamedWithRangeMultipart) { + auto res = + cli_.Get("/streamed-with-range", {{make_range_header({{1, 2}, {4, 5}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("267", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(267U, res->body.size()); +} + +TEST_F(ServerTest, GetStreamedWithTooManyRanges) { + Ranges ranges; + for (size_t i = 0; i < CPPHTTPLIB_RANGE_MAX_COUNT + 1; i++) { + ranges.emplace_back(0, -1); + } + + auto res = + cli_.Get("/streamed-with-range?error", {{make_range_header(ranges)}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(0U, res->body.size()); +} + +TEST_F(ServerTest, GetStreamedWithNonAscendingRanges) { + auto res = cli_.Get("/streamed-with-range?error", + {{make_range_header({{0, -1}, {0, -1}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(0U, res->body.size()); +} + +TEST_F(ServerTest, GetStreamedWithRangesMoreThanTwoOverwrapping) { + auto res = cli_.Get("/streamed-with-range?error", + {{make_range_header({{0, 1}, {1, 2}, {2, 3}, {3, 4}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); + EXPECT_EQ("0", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(0U, res->body.size()); +} + +TEST_F(ServerTest, GetStreamedEndless) { + uint64_t offset = 0; + auto res = cli_.Get("/streamed-cancel", + [&](const char * /*data*/, uint64_t data_length) { + if (offset < 100) { + offset += data_length; + return true; + } + return false; + }); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST_F(ServerTest, ClientStop) { + std::atomic_size_t count{4}; + std::vector threads; + + for (auto i = count.load(); i != 0; --i) { + threads.emplace_back([&]() { + auto res = cli_.Get("/streamed-cancel", + [&](const char *, uint64_t) { return true; }); + + --count; + + ASSERT_TRUE(!res); + EXPECT_TRUE(res.error() == Error::Canceled || + res.error() == Error::Read || res.error() == Error::Write); + }); + } + + std::this_thread::sleep_for(std::chrono::seconds(2)); + while (count != 0) { + cli_.stop(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + for (auto &t : threads) { + t.join(); + } +} + +TEST_F(ServerTest, GetWithRange1) { + auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("3", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 3-5/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("def"), res->body); +} + +TEST_F(ServerTest, GetWithRange2) { + auto res = cli_.Get("/with-range", {{make_range_header({{1, -1}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("6", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 1-6/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("bcdefg"), res->body); +} + +TEST_F(ServerTest, GetWithRange3) { + auto res = cli_.Get("/with-range", {{make_range_header({{0, 0}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("1", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 0-0/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("a"), res->body); +} + +TEST_F(ServerTest, GetWithRange4) { + auto res = cli_.Get("/with-range", {{make_range_header({{-1, 2}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("2", res->get_header_value("Content-Length")); + EXPECT_EQ(true, res->has_header("Content-Range")); + EXPECT_EQ("bytes 5-6/7", res->get_header_value("Content-Range")); + EXPECT_EQ(std::string("fg"), res->body); +} + +TEST_F(ServerTest, GetWithRangeOffsetGreaterThanContent) { + auto res = cli_.Get("/with-range", {{make_range_header({{10000, 20000}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); +} + +TEST_F(ServerTest, GetWithRangeMultipart) { + auto res = cli_.Get("/with-range", {{make_range_header({{1, 2}, {4, 5}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + EXPECT_EQ("267", res->get_header_value("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(267U, res->body.size()); +} + +TEST_F(ServerTest, GetWithRangeMultipartOffsetGreaterThanContent) { + auto res = + cli_.Get("/with-range", {{make_range_header({{-1, 2}, {10000, 30000}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::RangeNotSatisfiable_416, res->status); +} + +TEST_F(ServerTest, GetWithRangeCustomizedResponse) { + auto res = cli_.Get("/with-range-customized-response", {{make_range_header({{1, 2}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::BadRequest_400, res->status); + EXPECT_EQ(true, res->has_header("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(JSON_DATA, res->body); +} + +TEST_F(ServerTest, GetWithRangeMultipartCustomizedResponseMultipleRange) { + auto res = cli_.Get("/with-range-customized-response", {{make_range_header({{1, 2}, {4, 5}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::BadRequest_400, res->status); + EXPECT_EQ(true, res->has_header("Content-Length")); + EXPECT_EQ(false, res->has_header("Content-Range")); + EXPECT_EQ(JSON_DATA, res->body); +} + +TEST_F(ServerTest, Issue1772) { + auto res = cli_.Get("/issue1772", {{make_range_header({{1000, -1}})}}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); +} + +TEST_F(ServerTest, GetStreamedChunked) { + auto res = cli_.Get("/streamed-chunked"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); +} + +TEST_F(ServerTest, GetStreamedChunked2) { + auto res = cli_.Get("/streamed-chunked2"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); +} + +TEST_F(ServerTest, GetStreamedChunkedWithTrailer) { + auto res = cli_.Get("/streamed-chunked-with-trailer"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); + EXPECT_EQ(std::string("DummyVal1"), res->get_header_value("Dummy1")); + EXPECT_EQ(std::string("DummyVal2"), res->get_header_value("Dummy2")); +} + +TEST_F(ServerTest, LargeChunkedPost) { + Request req; + req.method = "POST"; + req.path = "/large-chunked"; + + std::string host_and_port; + host_and_port += HOST; + host_and_port += ":"; + host_and_port += std::to_string(PORT); + + req.headers.emplace("Host", host_and_port.c_str()); + req.headers.emplace("Accept", "*/*"); + req.headers.emplace("User-Agent", "cpp-httplib/0.1"); + req.headers.emplace("Content-Type", "text/plain"); + req.headers.emplace("Content-Length", "0"); + req.headers.emplace("Transfer-Encoding", "chunked"); + + std::string long_string(30 * 1024u, 'a'); + std::string chunk = "7800\r\n" + long_string + "\r\n"; + + // Attempt to make a large enough post to exceed OS buffers, to test that + // the server handles short reads if the full chunk data isn't available. + req.body = chunk + chunk + chunk + chunk + chunk + chunk + "0\r\n\r\n"; + + auto res = std::make_shared(); + auto error = Error::Success; + auto ret = cli_.send(req, *res, error); + + ASSERT_TRUE(ret); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, GetMethodRemoteAddr) { + auto res = cli_.Get("/remote_addr"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1"); +} + +TEST_F(ServerTest, GetMethodLocalAddr) { + auto res = cli_.Get("/local_addr"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_TRUE(res->body == std::string("::1:").append(to_string(PORT)) || + res->body == std::string("127.0.0.1:").append(to_string(PORT))); +} + +TEST_F(ServerTest, HTTPResponseSplitting) { + auto res = cli_.Get("/http_response_splitting"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, SlowRequest) { + request_threads_.emplace_back([this]() { auto res = cli_.Get("/slow"); }); + request_threads_.emplace_back([this]() { auto res = cli_.Get("/slow"); }); + request_threads_.emplace_back([this]() { auto res = cli_.Get("/slow"); }); +} + +#if 0 +TEST_F(ServerTest, SlowPost) { + char buffer[64 * 1024]; + memset(buffer, 0x42, sizeof(buffer)); + + auto res = cli_.Post( + "/slowpost", 64 * 1024 * 1024, + [&](size_t /*offset*/, size_t /*length*/, DataSink &sink) { + auto ret = sink.write(buffer, sizeof(buffer)); + EXPECT_TRUE(ret); + return true; + }, + "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, SlowPostFail) { + char buffer[64 * 1024]; + memset(buffer, 0x42, sizeof(buffer)); + + cli_.set_write_timeout(std::chrono::seconds(0)); + auto res = cli_.Post( + "/slowpost", 64 * 1024 * 1024, + [&](size_t /*offset*/, size_t /*length*/, DataSink &sink) { + sink.write(buffer, sizeof(buffer)); + return true; + }, + "text/plain"); + + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Write, res.error()); +} +#endif + +TEST_F(ServerTest, Put) { + auto res = cli_.Put("/put", "PUT", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PUT", res->body); +} + +TEST_F(ServerTest, PutWithContentProvider) { + auto res = cli_.Put( + "/put", 3, + [](size_t /*offset*/, size_t /*length*/, DataSink &sink) { + sink.os << "PUT"; + return true; + }, + "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PUT", res->body); +} + +TEST_F(ServerTest, PostWithContentProviderAbort) { + auto res = cli_.Post( + "/post", 42, + [](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) { + return false; + }, + "text/plain"); + + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST_F(ServerTest, PutWithContentProviderWithoutLength) { + auto res = cli_.Put( + "/put", + [](size_t /*offset*/, DataSink &sink) { + sink.os << "PUT"; + sink.done(); + return true; + }, + "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PUT", res->body); +} + +TEST_F(ServerTest, PostWithContentProviderWithoutLengthAbort) { + auto res = cli_.Post( + "/post", [](size_t /*offset*/, DataSink & /*sink*/) { return false; }, + "text/plain"); + + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +TEST_F(ServerTest, PutWithContentProviderWithGzip) { + cli_.set_compress(true); + auto res = cli_.Put( + "/put", 3, + [](size_t /*offset*/, size_t /*length*/, DataSink &sink) { + sink.os << "PUT"; + return true; + }, + "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PUT", res->body); +} + +TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) { + cli_.set_compress(true); + auto res = cli_.Post( + "/post", 42, + [](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) { + return false; + }, + "text/plain"); + + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST_F(ServerTest, PutWithContentProviderWithoutLengthWithGzip) { + cli_.set_compress(true); + auto res = cli_.Put( + "/put", + [](size_t /*offset*/, DataSink &sink) { + sink.os << "PUT"; + sink.done(); + return true; + }, + "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PUT", res->body); +} + +TEST_F(ServerTest, PostWithContentProviderWithoutLengthWithGzipAbort) { + cli_.set_compress(true); + auto res = cli_.Post( + "/post", [](size_t /*offset*/, DataSink & /*sink*/) { return false; }, + "text/plain"); + + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); +} + +TEST_F(ServerTest, PutLargeFileWithGzip) { + cli_.set_compress(true); + auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(LARGE_DATA, res->body); +} + +TEST_F(ServerTest, PutLargeFileWithGzip2) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string s = std::string("https://") + HOST + ":" + std::to_string(PORT); + Client cli(s.c_str()); + cli.enable_server_certificate_verification(false); +#else + std::string s = std::string("http://") + HOST + ":" + std::to_string(PORT); + Client cli(s.c_str()); +#endif + cli.set_compress(true); + + auto res = cli.Put("/put-large", LARGE_DATA, "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(LARGE_DATA, res->body); + EXPECT_EQ(101942u, res.get_request_header_value_u64("Content-Length")); + EXPECT_EQ("gzip", res.get_request_header_value("Content-Encoding")); +} + +TEST_F(ServerTest, PutContentWithDeflate) { + cli_.set_compress(false); + Headers headers; + headers.emplace("Content-Encoding", "deflate"); + // PUT in deflate format: + auto res = cli_.Put("/put", headers, + "\170\234\013\010\015\001\0\001\361\0\372", "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PUT", res->body); +} + +TEST_F(ServerTest, GetStreamedChunkedWithGzip) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + + auto res = cli_.Get("/streamed-chunked", headers); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); +} + +TEST_F(ServerTest, GetStreamedChunkedWithGzip2) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + + auto res = cli_.Get("/streamed-chunked2", headers); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); +} + +TEST_F(ServerTest, SplitDelimiterInPathRegex) { + auto res = cli_.Get("/regex-with-delimiter?key=^(?.*(value))"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(GzipDecompressor, ChunkedDecompression) { + std::string data; + for (size_t i = 0; i < 32 * 1024; ++i) { + data.push_back(static_cast('a' + i % 26)); + } + + std::string compressed_data; + { + httplib::detail::gzip_compressor compressor; + bool result = compressor.compress( + data.data(), data.size(), + /*last=*/true, + [&](const char *compressed_data_chunk, size_t compressed_data_size) { + compressed_data.insert(compressed_data.size(), compressed_data_chunk, + compressed_data_size); + return true; + }); + ASSERT_TRUE(result); + } + + std::string decompressed_data; + { + httplib::detail::gzip_decompressor decompressor; + + // Chunk size is chosen specifically to have a decompressed chunk size equal + // to 16384 bytes 16384 bytes is the size of decompressor output buffer + size_t chunk_size = 130; + for (size_t chunk_begin = 0; chunk_begin < compressed_data.size(); + chunk_begin += chunk_size) { + size_t current_chunk_size = + std::min(compressed_data.size() - chunk_begin, chunk_size); + bool result = decompressor.decompress( + compressed_data.data() + chunk_begin, current_chunk_size, + [&](const char *decompressed_data_chunk, + size_t decompressed_data_chunk_size) { + decompressed_data.insert(decompressed_data.size(), + decompressed_data_chunk, + decompressed_data_chunk_size); + return true; + }); + ASSERT_TRUE(result); + } + } + ASSERT_EQ(data, decompressed_data); +} + +TEST(GzipDecompressor, DeflateDecompression) { + std::string original_text = "Raw deflate without gzip"; + unsigned char data[32] = {0x78, 0x9C, 0x0B, 0x4A, 0x2C, 0x57, 0x48, 0x49, + 0x4D, 0xCB, 0x49, 0x2C, 0x49, 0x55, 0x28, 0xCF, + 0x2C, 0xC9, 0xC8, 0x2F, 0x2D, 0x51, 0x48, 0xAF, + 0xCA, 0x2C, 0x00, 0x00, 0x6F, 0x98, 0x09, 0x2E}; + std::string compressed_data(data, data + sizeof(data) / sizeof(data[0])); + + std::string decompressed_data; + { + httplib::detail::gzip_decompressor decompressor; + + bool result = decompressor.decompress( + compressed_data.data(), compressed_data.size(), + [&](const char *decompressed_data_chunk, + size_t decompressed_data_chunk_size) { + decompressed_data.insert(decompressed_data.size(), + decompressed_data_chunk, + decompressed_data_chunk_size); + return true; + }); + ASSERT_TRUE(result); + } + ASSERT_EQ(original_text, decompressed_data); +} + +TEST(GzipDecompressor, DeflateDecompressionTrailingBytes) { + std::string original_text = "Raw deflate without gzip"; + unsigned char data[40] = {0x78, 0x9C, 0x0B, 0x4A, 0x2C, 0x57, 0x48, 0x49, + 0x4D, 0xCB, 0x49, 0x2C, 0x49, 0x55, 0x28, 0xCF, + 0x2C, 0xC9, 0xC8, 0x2F, 0x2D, 0x51, 0x48, 0xAF, + 0xCA, 0x2C, 0x00, 0x00, 0x6F, 0x98, 0x09, 0x2E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::string compressed_data(data, data + sizeof(data) / sizeof(data[0])); + + std::string decompressed_data; + { + httplib::detail::gzip_decompressor decompressor; + + bool result = decompressor.decompress( + compressed_data.data(), compressed_data.size(), + [&](const char *decompressed_data_chunk, + size_t decompressed_data_chunk_size) { + decompressed_data.insert(decompressed_data.size(), + decompressed_data_chunk, + decompressed_data_chunk_size); + return true; + }); + ASSERT_TRUE(result); + } + ASSERT_EQ(original_text, decompressed_data); +} + +#ifdef _WIN32 +TEST(GzipDecompressor, LargeRandomData) { + + // prepare large random data that is difficult to be compressed and is + // expected to have large size even when compressed + std::random_device seed_gen; + std::mt19937 random(seed_gen()); + constexpr auto large_size_byte = 4294967296UL; // 4GiB + constexpr auto data_size = large_size_byte + 134217728UL; // + 128MiB + std::vector data(data_size / sizeof(std::uint32_t)); + std::generate(data.begin(), data.end(), [&]() { return random(); }); + + // compress data over 4GiB + std::string compressed_data; + compressed_data.reserve(large_size_byte + 536870912UL); // + 512MiB reserved + httplib::detail::gzip_compressor compressor; + auto result = compressor.compress(reinterpret_cast(data.data()), + data.size() * sizeof(std::uint32_t), true, + [&](const char *data, size_t size) { + compressed_data.insert( + compressed_data.size(), data, size); + return true; + }); + ASSERT_TRUE(result); + + // FIXME: compressed data size is expected to be greater than 4GiB, + // but there is no guarantee + // ASSERT_TRUE(compressed_data.size() >= large_size_byte); + + // decompress data over 4GiB + std::string decompressed_data; + decompressed_data.reserve(data_size); + httplib::detail::gzip_decompressor decompressor; + result = decompressor.decompress( + compressed_data.data(), compressed_data.size(), + [&](const char *data, size_t size) { + decompressed_data.insert(decompressed_data.size(), data, size); + return true; + }); + ASSERT_TRUE(result); + + // compare + ASSERT_EQ(data_size, decompressed_data.size()); + ASSERT_TRUE(std::memcmp(data.data(), decompressed_data.data(), data_size) == + 0); +} +#endif +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +TEST_F(ServerTest, GetStreamedChunkedWithBrotli) { + Headers headers; + headers.emplace("Accept-Encoding", "br"); + + auto res = cli_.Get("/streamed-chunked", headers); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); +} + +TEST_F(ServerTest, GetStreamedChunkedWithBrotli2) { + Headers headers; + headers.emplace("Accept-Encoding", "br"); + + auto res = cli_.Get("/streamed-chunked2", headers); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(std::string("123456789"), res->body); +} +#endif + +TEST_F(ServerTest, Patch) { + auto res = cli_.Patch("/patch", "PATCH", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("PATCH", res->body); +} + +TEST_F(ServerTest, Delete) { + auto res = cli_.Delete("/delete"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("DELETE", res->body); +} + +TEST_F(ServerTest, DeleteContentReceiver) { + auto res = cli_.Delete("/delete-body", "content", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("content", res->body); +} + +TEST_F(ServerTest, Options) { + auto res = cli_.Options("*"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("GET, POST, HEAD, OPTIONS", res->get_header_value("Allow")); + EXPECT_TRUE(res->body.empty()); +} + +TEST_F(ServerTest, URL) { + auto res = cli_.Get("/request-target?aaa=bbb&ccc=ddd"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, ArrayParam) { + auto res = cli_.Get("/array-param?array=value1&array=value2&array=value3"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, NoMultipleHeaders) { + Headers headers = {{"Content-Length", "5"}}; + auto res = cli_.Post("/validate-no-multiple-headers", headers, "hello", + "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, PostContentReceiver) { + auto res = cli_.Post("/content_receiver", "content", "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("content", res->body); +} + +TEST_F(ServerTest, PostMultipartFileContentReceiver) { + MultipartFormDataItems items = { + {"text1", "text default", "", ""}, + {"text2", "aωb", "", ""}, + {"file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain"}, + {"file2", "{\n \"world\", true\n}\n", "world.json", "application/json"}, + {"file3", "", "", "application/octet-stream"}, + }; + + auto res = cli_.Post("/content_receiver", items); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, PostMultipartPlusBoundary) { + MultipartFormDataItems items = { + {"text1", "text default", "", ""}, + {"text2", "aωb", "", ""}, + {"file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain"}, + {"file2", "{\n \"world\", true\n}\n", "world.json", "application/json"}, + {"file3", "", "", "application/octet-stream"}, + }; + + auto boundary = std::string("+++++"); + + std::string body; + + for (const auto &item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; + } + body += "--" + boundary + "--\r\n"; + + std::string content_type = "multipart/form-data; boundary=" + boundary; + auto res = cli_.Post("/content_receiver", body, content_type.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, PostContentReceiverGzip) { + cli_.set_compress(true); + auto res = cli_.Post("/content_receiver", "content", "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("content", res->body); +} + +TEST_F(ServerTest, PutContentReceiver) { + auto res = cli_.Put("/content_receiver", "content", "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("content", res->body); +} + +TEST_F(ServerTest, PatchContentReceiver) { + auto res = cli_.Patch("/content_receiver", "content", "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + ASSERT_EQ("content", res->body); +} + +TEST_F(ServerTest, PostQueryStringAndBody) { + auto res = + cli_.Post("/query-string-and-body?key=value", "content", "text/plain"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, HTTP2Magic) { + Request req; + req.method = "PRI"; + req.path = "*"; + req.body = "SM"; + + auto res = std::make_shared(); + auto error = Error::Success; + auto ret = cli_.send(req, *res, error); + + ASSERT_TRUE(ret); + EXPECT_EQ(StatusCode::BadRequest_400, res->status); +} + +TEST_F(ServerTest, KeepAlive) { + auto res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("Hello World!", res->body); + + res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("Hello World!", res->body); + + res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("Hello World!", res->body); + + res = cli_.Get("/not-exist"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + + res = cli_.Post("/empty", "", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("empty", res->body); + EXPECT_EQ("close", res->get_header_value("Connection")); + + res = cli_.Post( + "/empty", 0, [&](size_t, size_t, DataSink &) { return true; }, + "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("empty", res->body); + + cli_.set_keep_alive(false); + res = cli_.Get("/last-request"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("close", res->get_header_value("Connection")); +} + +TEST_F(ServerTest, TooManyRedirect) { + cli_.set_follow_location(true); + auto res = cli_.Get("/redirect/0"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::ExceedRedirectCount, res.error()); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +TEST_F(ServerTest, Gzip) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + auto res = cli_.Get("/compress", headers); + + ASSERT_TRUE(res); + EXPECT_EQ("gzip", res->get_header_value("Content-Encoding")); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("33", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, GzipWithoutAcceptEncoding) { + auto res = cli_.Get("/compress"); + + ASSERT_TRUE(res); + EXPECT_TRUE(res->get_header_value("Content-Encoding").empty()); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("100", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, GzipWithContentReceiver) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + std::string body; + auto res = cli_.Get("/compress", headers, + [&](const char *data, uint64_t data_length) { + EXPECT_EQ(100U, data_length); + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ("gzip", res->get_header_value("Content-Encoding")); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("33", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, GzipWithoutDecompressing) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + + cli_.set_decompress(false); + auto res = cli_.Get("/compress", headers); + + ASSERT_TRUE(res); + EXPECT_EQ("gzip", res->get_header_value("Content-Encoding")); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("33", res->get_header_value("Content-Length")); + EXPECT_EQ(33U, res->body.size()); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) { + std::string body; + auto res = cli_.Get("/compress", [&](const char *data, uint64_t data_length) { + EXPECT_EQ(100U, data_length); + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_TRUE(res->get_header_value("Content-Encoding").empty()); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("100", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, NoGzip) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + auto res = cli_.Get("/nocompress", headers); + + ASSERT_TRUE(res); + EXPECT_EQ(false, res->has_header("Content-Encoding")); + EXPECT_EQ("application/octet-stream", res->get_header_value("Content-Type")); + EXPECT_EQ("100", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, NoGzipWithContentReceiver) { + Headers headers; + headers.emplace("Accept-Encoding", "gzip, deflate"); + std::string body; + auto res = cli_.Get("/nocompress", headers, + [&](const char *data, uint64_t data_length) { + EXPECT_EQ(100U, data_length); + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(false, res->has_header("Content-Encoding")); + EXPECT_EQ("application/octet-stream", res->get_header_value("Content-Type")); + EXPECT_EQ("100", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST_F(ServerTest, MultipartFormDataGzip) { + MultipartFormDataItems items = { + {"key1", "test", "", ""}, + {"key2", "--abcdefg123", "", ""}, + }; + + cli_.set_compress(true); + auto res = cli_.Post("/compress-multipart", items); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +TEST_F(ServerTest, Brotli) { + Headers headers; + headers.emplace("Accept-Encoding", "br"); + auto res = cli_.Get("/compress", headers); + + ASSERT_TRUE(res); + EXPECT_EQ("br", res->get_header_value("Content-Encoding")); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("19", res->get_header_value("Content-Length")); + EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456" + "7890123456789012345678901234567890", + res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); +} +#endif + +// Sends a raw request to a server listening at HOST:PORT. +static bool send_request(time_t read_timeout_sec, const std::string &req, + std::string *resp = nullptr) { + auto error = Error::Success; + + auto client_sock = detail::create_client_socket( + HOST, "", PORT, AF_UNSPEC, false, nullptr, + /*connection_timeout_sec=*/5, 0, + /*read_timeout_sec=*/5, 0, + /*write_timeout_sec=*/5, 0, std::string(), error); + + if (client_sock == INVALID_SOCKET) { return false; } + + auto ret = detail::process_client_socket( + client_sock, read_timeout_sec, 0, 0, 0, [&](Stream &strm) { + if (req.size() != + static_cast(strm.write(req.data(), req.size()))) { + return false; + } + + char buf[512]; + + detail::stream_line_reader line_reader(strm, buf, sizeof(buf)); + while (line_reader.getline()) { + if (resp) { *resp += line_reader.ptr(); } + } + return true; + }); + + detail::close_socket(client_sock); + + return ret; +} + +TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) { + Server svr; + std::string header_value; + svr.Get("/validate-ws-in-headers", [&](const Request &req, Response &res) { + header_value = req.get_header_value("foo"); + res.set_content("ok", "text/plain"); + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + // Only space and horizontal tab are whitespace. Make sure other whitespace- + // like characters are not treated the same - use vertical tab and escape. + const std::string req = "GET /validate-ws-in-headers HTTP/1.1\r\n" + "foo: \t \v bar \x1B\t \r\n" + "Connection: close\r\n" + "\r\n"; + + ASSERT_TRUE(send_request(5, req)); + EXPECT_EQ(header_value, "\v bar \x1B"); +} + +// Sends a raw request and verifies that there isn't a crash or exception. +static void test_raw_request(const std::string &req, + std::string *out = nullptr) { + Server svr; + svr.Get("/hi", [&](const Request & /*req*/, Response &res) { + res.set_content("ok", "text/plain"); + }); + svr.Put("/put_hi", [&](const Request & /*req*/, Response &res) { + res.set_content("ok", "text/plain"); + }); + + // Server read timeout must be longer than the client read timeout for the + // bug to reproduce, probably to force the server to process a request + // without a trailing blank line. + const time_t client_read_timeout_sec = 1; + svr.set_read_timeout(std::chrono::seconds(client_read_timeout_sec + 1)); + bool listen_thread_ok = false; + thread t = thread([&] { listen_thread_ok = svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + EXPECT_TRUE(listen_thread_ok); + }); + + svr.wait_until_ready(); + + ASSERT_TRUE(send_request(client_read_timeout_sec, req, out)); +} + +TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) { + // A certain header line causes an exception if the header property is parsed + // naively with a single regex. This occurs with libc++ but not libstdc++. + test_raw_request( + "GET /hi HTTP/1.1\r\n" + " : " + " " + " "); +} + +TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) { + // A certain header line causes an exception if the header property *name* is + // parsed with a regular expression starting with "(.+?):" - this is a non- + // greedy matcher and requires backtracking when there are a lot of ":" + // characters. + // This occurs with libc++ but not libstdc++. + test_raw_request( + "GET /hi HTTP/1.1\r\n" + ":-:::::::::::::::::::::::::::-::::::::::::::::::::::::@-&&&&&&&&&&&" + "--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&" + "&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-:::::" + "::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::::::::::::::::::::" + ":::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::" + "::::::::-:::::::::::::::::@-&&&&&&&--:::::::-::::::::::::::::::::::" + ":::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::" + "::::::::::-:::::::::::::::::@-&&&&&::::::::::::-:::::::::::::::::@-" + "&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::" + ":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::" + "::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::@-&&" + "&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@" + "::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&" + "--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&" + "&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&" + "&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&" + "&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@" + "-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::" + "::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::" + ":::::@-&&&&&&&&&&&::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::" + ":::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::" + "::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-" + ":::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&---&&:&" + "&&.0------------:-:::::::::::::::::::::::::::::-:::::::::::::::::@-" + "&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::" + ":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::" + "::::@-&&&&&&&&&&&---&&:&&&.0------------O--------\rH PUTHTTP/1.1\r\n" + "&&&%%%"); +} + +TEST(ServerRequestParsingTest, ExcessiveWhitespaceInUnparsableHeaderLine) { + // Make sure this doesn't crash the server. + // In a previous version of the header line regex, the "\r" rendered the line + // unparsable and the regex engine repeatedly backtracked, trying to look for + // a new position where the leading white space ended and the field value + // began. + // The crash occurs with libc++ but not libstdc++. + test_raw_request("GET /hi HTTP/1.1\r\n" + "a:" + + std::string(2000, ' ') + '\r' + std::string(20, 'z') + + "\r\n" + "\r\n"); +} + +TEST(ServerRequestParsingTest, InvalidFirstChunkLengthInRequest) { + std::string out; + + test_raw_request("PUT /put_hi HTTP/1.1\r\n" + "Content-Type: text/plain\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "nothex\r\n", + &out); + EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); +} + +TEST(ServerRequestParsingTest, InvalidSecondChunkLengthInRequest) { + std::string out; + + test_raw_request("PUT /put_hi HTTP/1.1\r\n" + "Content-Type: text/plain\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "3\r\n" + "xyz\r\n" + "NaN\r\n", + &out); + EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); +} + +TEST(ServerRequestParsingTest, ChunkLengthTooHighInRequest) { + std::string out; + + test_raw_request("PUT /put_hi HTTP/1.1\r\n" + "Content-Type: text/plain\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + // Length is too large for 64 bits. + "1ffffffffffffffff\r\n" + "xyz\r\n", + &out); + EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); +} + +TEST(ServerRequestParsingTest, InvalidHeaderTextWithExtraCR) { + test_raw_request("GET /hi HTTP/1.1\r\n" + "Content-Type: text/plain\r\n\r"); +} + +TEST(ServerRequestParsingTest, InvalidSpaceInURL) { + std::string out; + test_raw_request("GET /h i HTTP/1.1\r\n\r\n", &out); + EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); +} + +TEST(ServerStopTest, StopServerWithChunkedTransmission) { + Server svr; + + svr.Get("/events", [](const Request & /*req*/, Response &res) { + res.set_header("Cache-Control", "no-cache"); + res.set_chunked_content_provider( + "text/event-stream", [](size_t offset, DataSink &sink) { + std::string s = "data:"; + s += std::to_string(offset); + s += "\n\n"; + auto ret = sink.write(s.data(), s.size()); + EXPECT_TRUE(ret); + std::this_thread::sleep_for(std::chrono::seconds(1)); + return true; + }); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + svr.wait_until_ready(); + + Client client(HOST, PORT); + const Headers headers = {{"Accept", "text/event-stream"}}; + + auto get_thread = std::thread([&client, &headers]() { + auto res = client.Get( + "/events", headers, + [](const char * /*data*/, size_t /*len*/) -> bool { return true; }); + }); + auto se = detail::scope_exit([&] { + svr.stop(); + get_thread.join(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + // Give GET time to get a few messages. + std::this_thread::sleep_for(std::chrono::seconds(2)); +} + +TEST(ServerStopTest, ClientAccessAfterServerDown) { + httplib::Server svr; + svr.Post("/hi", + [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.status = StatusCode::OK_200; + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + svr.wait_until_ready(); + + Client cli(HOST, PORT); + + auto res = cli.Post("/hi", "data", "text/plain"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + + res = cli.Post("/hi", "data", "text/plain"); + ASSERT_FALSE(res); +} + +TEST(ServerStopTest, ListenFailure) { + Server svr; + auto t = thread([&]() { + auto ret = svr.listen("????", PORT); + EXPECT_FALSE(ret); + }); + svr.wait_until_ready(); + svr.stop(); + t.join(); +} + +TEST(StreamingTest, NoContentLengthStreaming) { + Server svr; + + svr.Get("/stream", [](const Request & /*req*/, Response &res) { + res.set_content_provider("text/plain", [](size_t offset, DataSink &sink) { + if (offset < 6) { + sink.os << (offset < 3 ? "a" : "b"); + } else { + sink.done(); + } + return true; + }); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto listen_se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client client(HOST, PORT); + + auto get_thread = std::thread([&client]() { + std::string s; + auto res = + client.Get("/stream", [&s](const char *data, size_t len) -> bool { + s += std::string(data, len); + return true; + }); + EXPECT_EQ("aaabbb", s); + }); + auto get_se = detail::scope_exit([&] { get_thread.join(); }); + + // Give GET time to get a few messages. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +TEST(MountTest, Unmount) { + Server svr; + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + svr.set_mount_point("/mount2", "./www2"); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + + res = cli.Get("/mount2/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + + svr.set_mount_point("/", "./www"); + + res = cli.Get("/dir/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + + svr.remove_mount_point("/"); + res = cli.Get("/dir/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + + svr.remove_mount_point("/mount2"); + res = cli.Get("/mount2/dir/test.html"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); +} + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS +TEST(ExceptionTest, ThrowExceptionInHandler) { + Server svr; + + svr.Get("/exception", [&](const Request & /*req*/, Response & /*res*/) { + throw std::runtime_error("exception..."); + }); + + svr.Get("/unknown", [&](const Request & /*req*/, Response & /*res*/) { + throw std::runtime_error("exception\r\n..."); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + { + auto res = cli.Get("/exception"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::InternalServerError_500, res->status); + ASSERT_TRUE(res->has_header("EXCEPTION_WHAT")); + EXPECT_EQ("exception...", res->get_header_value("EXCEPTION_WHAT")); + } + + { + auto res = cli.Get("/unknown"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::InternalServerError_500, res->status); + ASSERT_TRUE(res->has_header("EXCEPTION_WHAT")); + EXPECT_EQ("exception\\r\\n...", res->get_header_value("EXCEPTION_WHAT")); + } +} +#endif + +TEST(KeepAliveTest, ReadTimeout) { + Server svr; + + svr.Get("/a", [&](const Request & /*req*/, Response &res) { + std::this_thread::sleep_for(std::chrono::seconds(2)); + res.set_content("a", "text/plain"); + }); + + svr.Get("/b", [&](const Request & /*req*/, Response &res) { + res.set_content("b", "text/plain"); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + cli.set_keep_alive(true); + cli.set_read_timeout(std::chrono::seconds(1)); + + auto resa = cli.Get("/a"); + ASSERT_FALSE(resa); + EXPECT_EQ(Error::Read, resa.error()); + + auto resb = cli.Get("/b"); + ASSERT_TRUE(resb); + EXPECT_EQ(StatusCode::OK_200, resb->status); + EXPECT_EQ("b", resb->body); +} + +TEST(KeepAliveTest, Issue1041) { + Server svr; + svr.set_keep_alive_timeout(3); + + svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + auto listen_thread = std::thread([&svr] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + cli.set_keep_alive(true); + + auto result = cli.Get("/hi"); + ASSERT_TRUE(result); + EXPECT_EQ(StatusCode::OK_200, result->status); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + + result = cli.Get("/hi"); + ASSERT_TRUE(result); + EXPECT_EQ(StatusCode::OK_200, result->status); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(KeepAliveTest, SSLClientReconnection) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(svr.is_valid()); + svr.set_keep_alive_timeout(1); + + svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + auto listen_thread = std::thread([&svr] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT); + cli.enable_server_certificate_verification(false); + cli.set_keep_alive(true); + + auto result = cli.Get("/hi"); + ASSERT_TRUE(result); + EXPECT_EQ(StatusCode::OK_200, result->status); + + result = cli.Get("/hi"); + ASSERT_TRUE(result); + EXPECT_EQ(StatusCode::OK_200, result->status); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + // Recoonect + result = cli.Get("/hi"); + ASSERT_TRUE(result); + EXPECT_EQ(StatusCode::OK_200, result->status); + + result = cli.Get("/hi"); + ASSERT_TRUE(result); + EXPECT_EQ(StatusCode::OK_200, result->status); +} +#endif + +TEST(ClientProblemDetectionTest, ContentProvider) { + Server svr; + + size_t content_length = 1024 * 1024; + + svr.Get("/hi", [&](const Request & /*req*/, Response &res) { + res.set_content_provider( + content_length, "text/plain", + [&](size_t offset, size_t length, DataSink &sink) { + auto out_len = std::min(length, static_cast(1024)); + std::string out(out_len, '@'); + sink.write(out.data(), out_len); + return offset < 4096; + }, + [](bool success) { ASSERT_FALSE(success); }); + }); + + svr.Get("/empty", [&](const Request & /*req*/, Response &res) { + res.set_content_provider( + 0, "text/plain", + [&](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) -> bool { + EXPECT_TRUE(false); + return true; + }, + [](bool success) { ASSERT_FALSE(success); }); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + { + auto res = cli.Get("/hi", [&](const char * /*data*/, + size_t /*data_length*/) { return false; }); + ASSERT_FALSE(res); + } + + { + auto res = cli.Get("/empty", [&](const char * /*data*/, + size_t /*data_length*/) { return false; }); + ASSERT_TRUE(res); + } +} + +TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) { + Server svr; + + svr.set_error_handler([](Request const &, Response &res) -> void { + res.set_chunked_content_provider( + "text/plain", [](std::size_t const, DataSink &sink) -> bool { + sink.os << "hello"; + sink.os << "world"; + sink.done(); + return true; + }); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::NotFound_404, res->status); + EXPECT_EQ("helloworld", res->body); +} + +TEST(LongPollingTest, ClientCloseDetection) { + Server svr; + + svr.Get("/events", [&](const Request & /*req*/, Response &res) { + res.set_chunked_content_provider( + "text/plain", [](std::size_t const, DataSink &sink) -> bool { + EXPECT_TRUE(sink.is_writable()); // the socket is alive + sink.os << "hello"; + + auto count = 10; + while (count > 0 && sink.is_writable()) { + this_thread::sleep_for(chrono::milliseconds(10)); + } + EXPECT_FALSE(sink.is_writable()); // the socket is closed + return true; + }); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + auto res = cli.Get("/events", [&](const char *data, size_t data_length) { + EXPECT_EQ("hello", string(data, data_length)); + return false; // close the socket immediately. + }); + + ASSERT_FALSE(res); +} + +TEST(GetWithParametersTest, GetWithParameters) { + Server svr; + + svr.Get("/", [&](const Request &req, Response &) { + EXPECT_EQ("world", req.get_param_value("hello")); + EXPECT_EQ("world2", req.get_param_value("hello2")); + EXPECT_EQ("world3", req.get_param_value("hello3")); + }); + + svr.Get("/params", [&](const Request &req, Response &) { + EXPECT_EQ("world", req.get_param_value("hello")); + EXPECT_EQ("world2", req.get_param_value("hello2")); + EXPECT_EQ("world3", req.get_param_value("hello3")); + }); + + svr.Get(R"(/resources/([a-z0-9\\-]+))", [&](const Request &req, Response &) { + EXPECT_EQ("resource-id", req.matches[1]); + EXPECT_EQ("foo", req.get_param_value("param1")); + EXPECT_EQ("bar", req.get_param_value("param2")); + }); + + svr.Get("/users/:id", [&](const Request &req, Response &) { + EXPECT_EQ("user-id", req.path_params.at("id")); + EXPECT_EQ("foo", req.get_param_value("param1")); + EXPECT_EQ("bar", req.get_param_value("param2")); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + + Params params; + params.emplace("hello", "world"); + params.emplace("hello2", "world2"); + params.emplace("hello3", "world3"); + auto res = cli.Get("/", params, Headers{}); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/params?hello=world&hello2=world2&hello3=world3"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/resources/resource-id?param1=foo¶m2=bar"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + Client cli(HOST, PORT); + + auto res = cli.Get("/users/user-id?param1=foo¶m2=bar"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + } +} + +TEST(GetWithParametersTest, GetWithParameters2) { + Server svr; + + svr.Get("/", [&](const Request &req, Response &res) { + auto text = req.get_param_value("hello"); + res.set_content(text, "text/plain"); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + Params params; + params.emplace("hello", "world"); + + std::string body; + auto res = cli.Get("/", params, Headers{}, + [&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("world", body); +} + +TEST(ClientDefaultHeadersTest, DefaultHeaders_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/range/32"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/range/32"}; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(host); +#else + Client cli(host); +#endif + + cli.set_default_headers({make_range_header({{1, 10}})}); + cli.set_connection_timeout(5); + + { + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ("bcdefghijk", res->body); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + } + + { + auto res = cli.Get(path); + ASSERT_TRUE(res); + EXPECT_EQ("bcdefghijk", res->body); + EXPECT_EQ(StatusCode::PartialContent_206, res->status); + } +} + +TEST(ServerDefaultHeadersTest, DefaultHeaders) { + Server svr; + svr.set_default_headers({{"Hello", "World"}}); + + svr.Get("/", [&](const Request & /*req*/, Response &res) { + res.set_content("ok", "text/plain"); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + auto res = cli.Get("/"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("ok", res->body); + EXPECT_EQ("World", res->get_header_value("Hello")); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(KeepAliveTest, ReadTimeoutSSL) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/a", [&](const Request & /*req*/, Response &res) { + std::this_thread::sleep_for(std::chrono::seconds(2)); + res.set_content("a", "text/plain"); + }); + + svr.Get("/b", [&](const Request & /*req*/, Response &res) { + res.set_content("b", "text/plain"); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli("localhost", PORT); + cli.enable_server_certificate_verification(false); + cli.set_keep_alive(true); + cli.set_read_timeout(std::chrono::seconds(1)); + + auto resa = cli.Get("/a"); + ASSERT_TRUE(!resa); + EXPECT_EQ(Error::Read, resa.error()); + + auto resb = cli.Get("/b"); + ASSERT_TRUE(resb); + EXPECT_EQ(StatusCode::OK_200, resb->status); + EXPECT_EQ("b", resb->body); +} +#endif + +class ServerTestWithAI_PASSIVE : public ::testing::Test { +protected: + ServerTestWithAI_PASSIVE() + : cli_(HOST, PORT) +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + , + svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE) +#endif + { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_.enable_server_certificate_verification(false); +#endif + } + + virtual void SetUp() { + svr_.Get("/hi", [&](const Request & /*req*/, Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + t_ = thread( + [&]() { ASSERT_TRUE(svr_.listen(std::string(), PORT, AI_PASSIVE)); }); + + svr_.wait_until_ready(); + } + + virtual void TearDown() { + svr_.stop(); + t_.join(); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli_; + SSLServer svr_; +#else + Client cli_; + Server svr_; +#endif + thread t_; +}; + +TEST_F(ServerTestWithAI_PASSIVE, GetMethod200) { + auto res = cli_.Get("/hi"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); + EXPECT_EQ("Hello World!", res->body); +} + +class ServerUpDownTest : public ::testing::Test { +protected: + ServerUpDownTest() : cli_(HOST, PORT) {} + + virtual void SetUp() { + t_ = thread([&]() { + svr_.bind_to_any_port(HOST); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + ASSERT_TRUE(svr_.listen_after_bind()); + }); + + svr_.wait_until_ready(); + } + + virtual void TearDown() { + svr_.stop(); + t_.join(); + } + + Client cli_; + Server svr_; + thread t_; +}; + +TEST_F(ServerUpDownTest, QuickStartStop) { + // Should not crash, especially when run with + // --gtest_filter=ServerUpDownTest.QuickStartStop --gtest_repeat=1000 +} + +class PayloadMaxLengthTest : public ::testing::Test { +protected: + PayloadMaxLengthTest() + : cli_(HOST, PORT) +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + , + svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE) +#endif + { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_.enable_server_certificate_verification(false); +#endif + } + + virtual void SetUp() { + svr_.set_payload_max_length(8); + + svr_.Post("/test", [&](const Request & /*req*/, Response &res) { + res.set_content("test", "text/plain"); + }); + + t_ = thread([&]() { ASSERT_TRUE(svr_.listen(HOST, PORT)); }); + + svr_.wait_until_ready(); + } + + virtual void TearDown() { + svr_.stop(); + t_.join(); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli_; + SSLServer svr_; +#else + Client cli_; + Server svr_; +#endif + thread t_; +}; + +TEST_F(PayloadMaxLengthTest, ExceedLimit) { + auto res = cli_.Post("/test", "123456789", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::PayloadTooLarge_413, res->status); + + res = cli_.Post("/test", "12345678", "text/plain"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(HostAndPortPropertiesTest, NoSSL) { + httplib::Client cli("www.google.com", 1234); + ASSERT_EQ("www.google.com", cli.host()); + ASSERT_EQ(1234, cli.port()); +} + +TEST(HostAndPortPropertiesTest, NoSSLWithSimpleAPI) { + httplib::Client cli("www.google.com:1234"); + ASSERT_EQ("www.google.com", cli.host()); + ASSERT_EQ(1234, cli.port()); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(HostAndPortPropertiesTest, SSL) { + httplib::SSLClient cli("www.google.com"); + ASSERT_EQ("www.google.com", cli.host()); + ASSERT_EQ(443, cli.port()); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(SSLClientTest, UpdateCAStore) { + httplib::SSLClient httplib_client("www.google.com"); + auto ca_store_1 = X509_STORE_new(); + X509_STORE_load_locations(ca_store_1, "/etc/ssl/certs/ca-certificates.crt", + nullptr); + httplib_client.set_ca_cert_store(ca_store_1); + + auto ca_store_2 = X509_STORE_new(); + X509_STORE_load_locations(ca_store_2, "/etc/ssl/certs/ca-certificates.crt", + nullptr); + httplib_client.set_ca_cert_store(ca_store_2); +} + +TEST(SSLClientTest, ServerNameIndication_Online) { +#ifdef CPPHTTPLIB_DEFAULT_HTTPBIN + auto host = "httpbin.org"; + auto path = std::string{"/get"}; +#else + auto host = "nghttp2.org"; + auto path = std::string{"/httpbin/get"}; +#endif + + SSLClient cli(host, 443); + auto res = cli.Get(path); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +TEST(SSLClientTest, ServerCertificateVerification1_Online) { + Client cli("https://google.com"); + auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::MovedPermanently_301, res->status); +} + +TEST(SSLClientTest, ServerCertificateVerification2_Online) { + SSLClient cli("google.com"); + cli.enable_server_certificate_verification(true); + cli.set_ca_cert_path("hello"); + auto res = cli.Get("/"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::SSLLoadingCerts, res.error()); +} + +TEST(SSLClientTest, ServerCertificateVerification3_Online) { + SSLClient cli("google.com"); + cli.set_ca_cert_path(CA_CERT_FILE); + auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::MovedPermanently_301, res->status); +} + +TEST(SSLClientTest, ServerCertificateVerification4) { + SSLServer svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &, Response &res) { + res.set_content("test", "text/plain"); + svr.stop(); + ASSERT_TRUE(true); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); }); + auto se = detail::scope_exit([&] { + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli("127.0.0.1", PORT); + cli.set_ca_cert_path(SERVER_CERT2_FILE); + cli.enable_server_certificate_verification(true); + cli.set_connection_timeout(30); + + auto res = cli.Get("/test"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +TEST(SSLClientTest, ServerCertificateVerification5_Online) { + std::string cert; + detail::read_file(CA_CERT_FILE, cert); + + SSLClient cli("google.com"); + cli.load_ca_cert_store(cert.data(), cert.size()); + const auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::MovedPermanently_301, res->status); +} + +TEST(SSLClientTest, ServerCertificateVerification6_Online) { + // clang-format off + static constexpr char cert[] = + "GlobalSign Root CA\n" + "==================\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\n" + "GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\n" + "b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\n" + "BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\n" + "VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\n" + "DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\n" + "THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\n" + "Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\n" + "c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\n" + "gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\n" + "AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\n" + "Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\n" + "j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\n" + "hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\n" + "X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n" + "-----END CERTIFICATE-----\n"; + // clang-format on + + SSLClient cli("google.com"); + cli.load_ca_cert_store(cert, sizeof(cert)); + const auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::MovedPermanently_301, res->status); +} + +TEST(SSLClientTest, WildcardHostNameMatch_Online) { + SSLClient cli("www.youtube.com"); + + cli.set_ca_cert_path(CA_CERT_FILE); + cli.enable_server_certificate_verification(true); + cli.set_follow_location(true); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +#if 0 +TEST(SSLClientTest, SetInterfaceWithINET6) { + auto cli = std::make_shared("https://httpbin.org"); + ASSERT_TRUE(cli != nullptr); + + cli->set_address_family(AF_INET6); + cli->set_interface("en0"); + + auto res = cli->Get("/get"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} +#endif + +void ClientCertPresent( + const std::string &client_cert_file, + const std::string &client_private_key_file, + const std::string &client_encrypted_private_key_pass = std::string()) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, + CLIENT_CA_CERT_DIR); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &req, Response &res) { + res.set_content("test", "text/plain"); + + auto peer_cert = SSL_get_peer_certificate(req.ssl); + ASSERT_TRUE(peer_cert != nullptr); + + auto subject_name = X509_get_subject_name(peer_cert); + ASSERT_TRUE(subject_name != nullptr); + + std::string common_name; + { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + common_name.assign(name, static_cast(name_len)); + } + + EXPECT_EQ("Common Name", common_name); + + X509_free(peer_cert); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT, client_cert_file, client_private_key_file, + client_encrypted_private_key_pass); + cli.enable_server_certificate_verification(false); + cli.set_connection_timeout(30); + + auto res = cli.Get("/test"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +TEST(SSLClientServerTest, ClientCertPresent) { + ClientCertPresent(CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE); +} + +TEST(SSLClientServerTest, ClientEncryptedCertPresent) { + ClientCertPresent(CLIENT_ENCRYPTED_CERT_FILE, + CLIENT_ENCRYPTED_PRIVATE_KEY_FILE, + CLIENT_ENCRYPTED_PRIVATE_KEY_PASS); +} + +#if !defined(_WIN32) || defined(OPENSSL_USE_APPLINK) +void MemoryClientCertPresent( + const std::string &client_cert_file, + const std::string &client_private_key_file, + const std::string &client_encrypted_private_key_pass = std::string()) { + auto f = fopen(SERVER_CERT_FILE, "r+"); + auto server_cert = PEM_read_X509(f, nullptr, nullptr, nullptr); + fclose(f); + + f = fopen(SERVER_PRIVATE_KEY_FILE, "r+"); + auto server_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr); + fclose(f); + + f = fopen(CLIENT_CA_CERT_FILE, "r+"); + auto client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr); + auto client_ca_cert_store = X509_STORE_new(); + X509_STORE_add_cert(client_ca_cert_store, client_cert); + X509_free(client_cert); + fclose(f); + + f = fopen(client_cert_file.c_str(), "r+"); + client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr); + fclose(f); + + f = fopen(client_private_key_file.c_str(), "r+"); + auto client_private_key = PEM_read_PrivateKey( + f, nullptr, nullptr, (void *)client_encrypted_private_key_pass.c_str()); + fclose(f); + + SSLServer svr(server_cert, server_private_key, client_ca_cert_store); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &req, Response &res) { + res.set_content("test", "text/plain"); + + auto peer_cert = SSL_get_peer_certificate(req.ssl); + ASSERT_TRUE(peer_cert != nullptr); + + auto subject_name = X509_get_subject_name(peer_cert); + ASSERT_TRUE(subject_name != nullptr); + + std::string common_name; + { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + common_name.assign(name, static_cast(name_len)); + } + + EXPECT_EQ("Common Name", common_name); + + X509_free(peer_cert); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT, client_cert, client_private_key, + client_encrypted_private_key_pass); + cli.enable_server_certificate_verification(false); + cli.set_connection_timeout(30); + + auto res = cli.Get("/test"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + + X509_free(server_cert); + EVP_PKEY_free(server_private_key); + X509_free(client_cert); + EVP_PKEY_free(client_private_key); +} + +TEST(SSLClientServerTest, MemoryClientCertPresent) { + MemoryClientCertPresent(CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE); +} + +TEST(SSLClientServerTest, MemoryClientEncryptedCertPresent) { + MemoryClientCertPresent(CLIENT_ENCRYPTED_CERT_FILE, + CLIENT_ENCRYPTED_PRIVATE_KEY_FILE, + CLIENT_ENCRYPTED_PRIVATE_KEY_PASS); +} +#endif + +TEST(SSLClientServerTest, ClientCertMissing) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, + CLIENT_CA_CERT_DIR); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &, Response &) { ASSERT_TRUE(false); }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT); + auto res = cli.Get("/test"); + cli.set_connection_timeout(30); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::SSLServerVerification, res.error()); +} + +TEST(SSLClientServerTest, TrustDirOptional) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &, Response &res) { + res.set_content("test", "text/plain"); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE); + cli.enable_server_certificate_verification(false); + cli.set_connection_timeout(30); + + auto res = cli.Get("/test"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +TEST(SSLClientServerTest, SSLConnectTimeout) { + class NoListenSSLServer : public SSLServer { + public: + NoListenSSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path, + const char *client_ca_cert_dir_path = nullptr) + : SSLServer(cert_path, private_key_path, client_ca_cert_file_path, + client_ca_cert_dir_path), + stop_(false) {} + + std::atomic_bool stop_; + + private: + bool process_and_close_socket(socket_t /*sock*/) override { + // Don't create SSL context + while (!stop_.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return true; + } + }; + NoListenSSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, + CLIENT_CA_CERT_FILE); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &, Response &res) { + res.set_content("test", "text/plain"); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); }); + auto se = detail::scope_exit([&] { + svr.stop_ = true; + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE); + cli.enable_server_certificate_verification(false); + cli.set_connection_timeout(1); + + auto res = cli.Get("/test"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::SSLConnection, res.error()); +} + +TEST(SSLClientServerTest, CustomizeServerSSLCtx) { + auto setup_ssl_ctx_callback = [](SSL_CTX &ssl_ctx) { + SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_COMPRESSION); + SSL_CTX_set_options(&ssl_ctx, + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_TLSv1); + SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_TLSv1_1); + auto ciphers = "ECDHE-RSA-AES128-SHA256:" + "ECDHE-DSS-AES128-SHA256:" + "ECDHE-RSA-AES256-SHA256:" + "ECDHE-DSS-AES256-SHA256:"; + SSL_CTX_set_cipher_list(&ssl_ctx, ciphers); + if (SSL_CTX_use_certificate_chain_file(&ssl_ctx, SERVER_CERT_FILE) != 1 || + SSL_CTX_use_PrivateKey_file(&ssl_ctx, SERVER_PRIVATE_KEY_FILE, + SSL_FILETYPE_PEM) != 1) { + return false; + } + SSL_CTX_load_verify_locations(&ssl_ctx, CLIENT_CA_CERT_FILE, + CLIENT_CA_CERT_DIR); + SSL_CTX_set_verify( + &ssl_ctx, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + return true; + }; + + SSLServer svr(setup_ssl_ctx_callback); + ASSERT_TRUE(svr.is_valid()); + + svr.Get("/test", [&](const Request &req, Response &res) { + res.set_content("test", "text/plain"); + + auto peer_cert = SSL_get_peer_certificate(req.ssl); + ASSERT_TRUE(peer_cert != nullptr); + + auto subject_name = X509_get_subject_name(peer_cert); + ASSERT_TRUE(subject_name != nullptr); + + std::string common_name; + { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + common_name.assign(name, static_cast(name_len)); + } + + EXPECT_EQ("Common Name", common_name); + + X509_free(peer_cert); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE); + cli.enable_server_certificate_verification(false); + cli.set_connection_timeout(30); + + auto res = cli.Get("/test"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +// Disabled due to the out-of-memory problem on GitHub Actions Workflows +TEST(SSLClientServerTest, DISABLED_LargeDataTransfer) { + + // prepare large data + std::random_device seed_gen; + std::mt19937 random(seed_gen()); + constexpr auto large_size_byte = 2147483648UL + 1048576UL; // 2GiB + 1MiB + std::vector binary(large_size_byte / sizeof(std::uint32_t)); + std::generate(binary.begin(), binary.end(), [&random]() { return random(); }); + + // server + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(svr.is_valid()); + + svr.Post("/binary", [&](const Request &req, Response &res) { + EXPECT_EQ(large_size_byte, req.body.size()); + EXPECT_EQ(0, std::memcmp(binary.data(), req.body.data(), large_size_byte)); + res.set_content(req.body, "application/octet-stream"); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + // client POST + SSLClient cli("localhost", PORT); + cli.enable_server_certificate_verification(false); + cli.set_read_timeout(std::chrono::seconds(100)); + cli.set_write_timeout(std::chrono::seconds(100)); + auto res = cli.Post("/binary", reinterpret_cast(binary.data()), + large_size_byte, "application/octet-stream"); + + // compare + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(large_size_byte, res->body.size()); + EXPECT_EQ(0, std::memcmp(binary.data(), res->body.data(), large_size_byte)); +} +#endif + +#ifdef _WIN32 +TEST(CleanupTest, WSACleanup) { + int ret = WSACleanup(); + ASSERT_EQ(0, ret); +} +#endif + +#ifndef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(NoSSLSupport, SimpleInterface) { + ASSERT_ANY_THROW(Client cli("https://yahoo.com")); +} +#endif + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS +TEST(InvalidScheme, SimpleInterface) { + ASSERT_ANY_THROW(Client cli("scheme://yahoo.com")); +} +#endif + +TEST(NoScheme, SimpleInterface) { + Client cli("yahoo.com:80"); + ASSERT_TRUE(cli.is_valid()); +} + +TEST(SendAPI, SimpleInterface_Online) { + Client cli("http://yahoo.com"); + + Request req; + req.method = "GET"; + req.path = "/"; + auto res = cli.send(req); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); +} + +TEST(ClientImplMethods, GetSocketTest) { + httplib::Server svr; + svr.Get("/", [&](const httplib::Request & /*req*/, httplib::Response &res) { + res.status = StatusCode::OK_200; + }); + + auto thread = std::thread([&]() { svr.listen("127.0.0.1", 3333); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + httplib::Client cli("http://127.0.0.1:3333"); + cli.set_keep_alive(true); + + // Use the behavior of cpp-httplib of opening the connection + // only when the first request happens. If that changes, + // this test would be obsolete. + + EXPECT_EQ(cli.socket(), INVALID_SOCKET); + + // This also implicitly tests the server. But other tests would fail much + // earlier than this one to be considered. + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + + EXPECT_EQ(StatusCode::OK_200, res->status); + ASSERT_TRUE(cli.socket() != INVALID_SOCKET); + } +} + +// Disabled due to out-of-memory problem on GitHub Actions +#ifdef _WIN64 +TEST(ServerLargeContentTest, DISABLED_SendLargeContent) { + // allocate content size larger than 2GB in memory + const size_t content_size = 2LL * 1024LL * 1024LL * 1024LL + 1LL; + char *content = (char *)malloc(content_size); + ASSERT_TRUE(content); + + Server svr; + svr.Get("/foo", + [=](const httplib::Request & /*req*/, httplib::Response &res) { + res.set_content(content, content_size, "application/octet-stream"); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + if (content) free(content); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli(HOST, PORT); + auto res = cli.Get("/foo"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(content_size, res->body.length()); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(YahooRedirectTest2, SimpleInterface_Online) { + Client cli("http://yahoo.com"); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); + + cli.set_follow_location(true); + res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("https://www.yahoo.com/", res->location); +} + +TEST(YahooRedirectTest3, SimpleInterface_Online) { + Client cli("https://yahoo.com"); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); + + cli.set_follow_location(true); + res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("https://www.yahoo.com/", res->location); +} + +TEST(YahooRedirectTest3, NewResultInterface_Online) { + Client cli("https://yahoo.com"); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_FALSE(!res); + ASSERT_TRUE(res); + ASSERT_FALSE(res == nullptr); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(Error::Success, res.error()); + EXPECT_EQ(StatusCode::MovedPermanently_301, res.value().status); + EXPECT_EQ(StatusCode::MovedPermanently_301, (*res).status); + EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); + + cli.set_follow_location(true); + res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(Error::Success, res.error()); + EXPECT_EQ(StatusCode::OK_200, res.value().status); + EXPECT_EQ(StatusCode::OK_200, (*res).status); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("https://www.yahoo.com/", res->location); +} + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +TEST(DecodeWithChunkedEncoding, BrotliEncoding_Online) { + Client cli("https://cdnjs.cloudflare.com"); + auto res = + cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "br"}}); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ(287630U, res->body.size()); + EXPECT_EQ("application/javascript; charset=utf-8", + res->get_header_value("Content-Type")); +} +#endif + +TEST(HttpsToHttpRedirectTest, SimpleInterface_Online) { + Client cli("https://nghttp2.org"); + cli.set_follow_location(true); + auto res = + cli.Get("/httpbin/" + "redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302"); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(HttpsToHttpRedirectTest2, SimpleInterface_Online) { + Client cli("https://nghttp2.org"); + cli.set_follow_location(true); + + Params params; + params.emplace("url", "http://www.google.com"); + params.emplace("status_code", "302"); + + auto res = cli.Get("/httpbin/redirect-to", params, Headers{}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(HttpsToHttpRedirectTest3, SimpleInterface_Online) { + Client cli("https://nghttp2.org"); + cli.set_follow_location(true); + + Params params; + params.emplace("url", "http://www.google.com"); + + auto res = cli.Get("/httpbin/redirect-to?status_code=302", params, Headers{}); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(HttpToHttpsRedirectTest, CertFile) { + Server svr; + ASSERT_TRUE(svr.is_valid()); + svr.Get("/index", [&](const Request &, Response &res) { + res.set_redirect("https://127.0.0.1:1235/index"); + svr.stop(); + }); + + SSLServer ssl_svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(ssl_svr.is_valid()); + ssl_svr.Get("/index", [&](const Request &, Response &res) { + res.set_content("test", "text/plain"); + ssl_svr.stop(); + }); + + thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); }); + thread t2 = thread([&]() { ASSERT_TRUE(ssl_svr.listen("127.0.0.1", 1235)); }); + auto se = detail::scope_exit([&] { + t2.join(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + ssl_svr.wait_until_ready(); + + Client cli("127.0.0.1", PORT); + cli.set_ca_cert_path(SERVER_CERT2_FILE); + cli.enable_server_certificate_verification(true); + cli.set_follow_location(true); + cli.set_connection_timeout(30); + + auto res = cli.Get("/index"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); +} + +TEST(MultipartFormDataTest, LargeData) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + + svr.Post("/post", [&](const Request &req, Response & /*res*/, + const ContentReader &content_reader) { + if (req.is_multipart_form_data()) { + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + EXPECT_TRUE(std::string(files[0].name) == "document"); + EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size()); + EXPECT_TRUE(files[0].filename == "2MB_data"); + EXPECT_TRUE(files[0].content_type == "application/octet-stream"); + + EXPECT_TRUE(files[1].name == "hello"); + EXPECT_TRUE(files[1].content == "world"); + EXPECT_TRUE(files[1].filename == ""); + EXPECT_TRUE(files[1].content_type == ""); + } else { + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + } + }); + + auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + std::string data(1024 * 1024 * 2, '.'); + std::stringstream buffer; + buffer << data; + + Client cli("https://localhost:8080"); + cli.enable_server_certificate_verification(false); + + MultipartFormDataItems items{ + {"document", buffer.str(), "2MB_data", "application/octet-stream"}, + {"hello", "world", "", ""}, + }; + + auto res = cli.Post("/post", items); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } +} + +TEST(MultipartFormDataTest, DataProviderItems) { + + std::random_device seed_gen; + std::mt19937 random(seed_gen()); + + std::string rand1; + rand1.resize(1000); + std::generate(rand1.begin(), rand1.end(), [&]() { return random(); }); + + std::string rand2; + rand2.resize(3000); + std::generate(rand2.begin(), rand2.end(), [&]() { return random(); }); + + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + + svr.Post("/post-none", [&](const Request &req, Response & /*res*/, + const ContentReader &content_reader) { + ASSERT_FALSE(req.is_multipart_form_data()); + + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + + EXPECT_EQ(body, ""); + }); + + svr.Post("/post-items", [&](const Request &req, Response & /*res*/, + const ContentReader &content_reader) { + ASSERT_TRUE(req.is_multipart_form_data()); + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + ASSERT_TRUE(files.size() == 2); + + EXPECT_EQ(std::string(files[0].name), "name1"); + EXPECT_EQ(files[0].content, "Testing123"); + EXPECT_EQ(files[0].filename, "filename1"); + EXPECT_EQ(files[0].content_type, "application/octet-stream"); + + EXPECT_EQ(files[1].name, "name2"); + EXPECT_EQ(files[1].content, "Testing456"); + EXPECT_EQ(files[1].filename, ""); + EXPECT_EQ(files[1].content_type, ""); + }); + + svr.Post("/post-providers", [&](const Request &req, Response & /*res*/, + const ContentReader &content_reader) { + ASSERT_TRUE(req.is_multipart_form_data()); + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + ASSERT_TRUE(files.size() == 2); + + EXPECT_EQ(files[0].name, "name3"); + EXPECT_EQ(files[0].content, rand1); + EXPECT_EQ(files[0].filename, "filename3"); + EXPECT_EQ(files[0].content_type, ""); + + EXPECT_EQ(files[1].name, "name4"); + EXPECT_EQ(files[1].content, rand2); + EXPECT_EQ(files[1].filename, "filename4"); + EXPECT_EQ(files[1].content_type, ""); + }); + + svr.Post("/post-both", [&](const Request &req, Response & /*res*/, + const ContentReader &content_reader) { + ASSERT_TRUE(req.is_multipart_form_data()); + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + ASSERT_TRUE(files.size() == 4); + + EXPECT_EQ(std::string(files[0].name), "name1"); + EXPECT_EQ(files[0].content, "Testing123"); + EXPECT_EQ(files[0].filename, "filename1"); + EXPECT_EQ(files[0].content_type, "application/octet-stream"); + + EXPECT_EQ(files[1].name, "name2"); + EXPECT_EQ(files[1].content, "Testing456"); + EXPECT_EQ(files[1].filename, ""); + EXPECT_EQ(files[1].content_type, ""); + + EXPECT_EQ(files[2].name, "name3"); + EXPECT_EQ(files[2].content, rand1); + EXPECT_EQ(files[2].filename, "filename3"); + EXPECT_EQ(files[2].content_type, ""); + + EXPECT_EQ(files[3].name, "name4"); + EXPECT_EQ(files[3].content, rand2); + EXPECT_EQ(files[3].filename, "filename4"); + EXPECT_EQ(files[3].content_type, ""); + }); + + auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli("https://localhost:8080"); + cli.enable_server_certificate_verification(false); + + MultipartFormDataItems items{ + {"name1", "Testing123", "filename1", "application/octet-stream"}, + {"name2", "Testing456", "", ""}, // not a file + }; + + { + auto res = cli.Post("/post-none", {}, {}, {}); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } + + MultipartFormDataProviderItems providers; + + { + auto res = + cli.Post("/post-items", {}, items, providers); // empty providers + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } + + providers.push_back({"name3", + [&](size_t offset, httplib::DataSink &sink) -> bool { + // test the offset is given correctly at each step + if (!offset) + sink.os.write(rand1.data(), 30); + else if (offset == 30) + sink.os.write(rand1.data() + 30, 300); + else if (offset == 330) + sink.os.write(rand1.data() + 330, 670); + else if (offset == rand1.size()) + sink.done(); + return true; + }, + "filename3", + {}}); + + providers.push_back({"name4", + [&](size_t offset, httplib::DataSink &sink) -> bool { + // test the offset is given correctly at each step + if (!offset) + sink.os.write(rand2.data(), 2000); + else if (offset == 2000) + sink.os.write(rand2.data() + 2000, 1); + else if (offset == 2001) + sink.os.write(rand2.data() + 2001, 999); + else if (offset == rand2.size()) + sink.done(); + return true; + }, + "filename4", + {}}); + + { + auto res = cli.Post("/post-providers", {}, {}, providers); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } + + { + auto res = cli.Post("/post-both", {}, items, providers); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } + } +} + +TEST(MultipartFormDataTest, BadHeader) { + Server svr; + svr.Post("/post", [&](const Request & /*req*/, Response &res) { + res.set_content("ok", "text/plain"); + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + const std::string body = + "This is the preamble. It is to be ignored, though it\r\n" + "is a handy place for composition agents to include an\r\n" + "explanatory note to non-MIME conformant readers.\r\n" + "\r\n" + "\r\n" + "--simple boundary\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + ": BAD...\r\n" + "\r\n" + "value1\r\n" + "--simple boundary\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"example.txt\"\r\n" + "\r\n" + "value2\r\n" + "--simple boundary--\r\n" + "This is the epilogue. It is also to be ignored.\r\n"; + + std::string content_type = + R"(multipart/form-data; boundary="simple boundary")"; + + Client cli(HOST, PORT); + auto res = cli.Post("/post", body, content_type.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::BadRequest_400, res->status); +} + +TEST(MultipartFormDataTest, WithPreamble) { + Server svr; + svr.Post("/post", [&](const Request & /*req*/, Response &res) { + res.set_content("ok", "text/plain"); + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + const std::string body = + "This is the preamble. It is to be ignored, though it\r\n" + "is a handy place for composition agents to include an\r\n" + "explanatory note to non-MIME conformant readers.\r\n" + "\r\n" + "\r\n" + "--simple boundary\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "value1\r\n" + "--simple boundary\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"example.txt\"\r\n" + "\r\n" + "value2\r\n" + "--simple boundary--\r\n" + "This is the epilogue. It is also to be ignored.\r\n"; + + std::string content_type = + R"(multipart/form-data; boundary="simple boundary")"; + + Client cli(HOST, PORT); + auto res = cli.Post("/post", body, content_type.c_str()); + + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(MultipartFormDataTest, PostCustomBoundary) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + + svr.Post("/post_customboundary", [&](const Request &req, Response & /*res*/, + const ContentReader &content_reader) { + if (req.is_multipart_form_data()) { + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + EXPECT_TRUE(std::string(files[0].name) == "document"); + EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size()); + EXPECT_TRUE(files[0].filename == "2MB_data"); + EXPECT_TRUE(files[0].content_type == "application/octet-stream"); + + EXPECT_TRUE(files[1].name == "hello"); + EXPECT_TRUE(files[1].content == "world"); + EXPECT_TRUE(files[1].filename == ""); + EXPECT_TRUE(files[1].content_type == ""); + } else { + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + } + }); + + auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + std::string data(1024 * 1024 * 2, '.'); + std::stringstream buffer; + buffer << data; + + Client cli("https://localhost:8080"); + cli.enable_server_certificate_verification(false); + + MultipartFormDataItems items{ + {"document", buffer.str(), "2MB_data", "application/octet-stream"}, + {"hello", "world", "", ""}, + }; + + auto res = cli.Post("/post_customboundary", {}, items, "abc-abc"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } +} + +TEST(MultipartFormDataTest, PostInvalidBoundaryChars) { + std::string data(1024 * 1024 * 2, '&'); + std::stringstream buffer; + buffer << data; + + Client cli("https://localhost:8080"); + + MultipartFormDataItems items{ + {"document", buffer.str(), "2MB_data", "application/octet-stream"}, + {"hello", "world", "", ""}, + }; + + for (const char &c : " \t\r\n") { + auto res = + cli.Post("/invalid_boundary", {}, items, string("abc123").append(1, c)); + ASSERT_EQ(Error::UnsupportedMultipartBoundaryChars, res.error()); + ASSERT_FALSE(res); + } +} + +TEST(MultipartFormDataTest, PutFormData) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + + svr.Put("/put", [&](const Request &req, const Response & /*res*/, + const ContentReader &content_reader) { + if (req.is_multipart_form_data()) { + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + EXPECT_TRUE(std::string(files[0].name) == "document"); + EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size()); + EXPECT_TRUE(files[0].filename == "2MB_data"); + EXPECT_TRUE(files[0].content_type == "application/octet-stream"); + + EXPECT_TRUE(files[1].name == "hello"); + EXPECT_TRUE(files[1].content == "world"); + EXPECT_TRUE(files[1].filename == ""); + EXPECT_TRUE(files[1].content_type == ""); + } else { + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + } + }); + + auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + std::string data(1024 * 1024 * 2, '&'); + std::stringstream buffer; + buffer << data; + + Client cli("https://localhost:8080"); + cli.enable_server_certificate_verification(false); + + MultipartFormDataItems items{ + {"document", buffer.str(), "2MB_data", "application/octet-stream"}, + {"hello", "world", "", ""}, + }; + + auto res = cli.Put("/put", items); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } +} + +TEST(MultipartFormDataTest, PutFormDataCustomBoundary) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + + svr.Put("/put_customboundary", + [&](const Request &req, const Response & /*res*/, + const ContentReader &content_reader) { + if (req.is_multipart_form_data()) { + MultipartFormDataItems files; + content_reader( + [&](const MultipartFormData &file) { + files.push_back(file); + return true; + }, + [&](const char *data, size_t data_length) { + files.back().content.append(data, data_length); + return true; + }); + + EXPECT_TRUE(std::string(files[0].name) == "document"); + EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size()); + EXPECT_TRUE(files[0].filename == "2MB_data"); + EXPECT_TRUE(files[0].content_type == "application/octet-stream"); + + EXPECT_TRUE(files[1].name == "hello"); + EXPECT_TRUE(files[1].content == "world"); + EXPECT_TRUE(files[1].filename == ""); + EXPECT_TRUE(files[1].content_type == ""); + } else { + std::string body; + content_reader([&](const char *data, size_t data_length) { + body.append(data, data_length); + return true; + }); + } + }); + + auto t = std::thread([&]() { svr.listen("localhost", 8080); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + std::string data(1024 * 1024 * 2, '&'); + std::stringstream buffer; + buffer << data; + + Client cli("https://localhost:8080"); + cli.enable_server_certificate_verification(false); + + MultipartFormDataItems items{ + {"document", buffer.str(), "2MB_data", "application/octet-stream"}, + {"hello", "world", "", ""}, + }; + + auto res = cli.Put("/put_customboundary", {}, items, "abc-abc_"); + ASSERT_TRUE(res); + ASSERT_EQ(StatusCode::OK_200, res->status); + } +} + +TEST(MultipartFormDataTest, PutInvalidBoundaryChars) { + std::string data(1024 * 1024 * 2, '&'); + std::stringstream buffer; + buffer << data; + + Client cli("https://localhost:8080"); + cli.enable_server_certificate_verification(false); + + MultipartFormDataItems items{ + {"document", buffer.str(), "2MB_data", "application/octet-stream"}, + {"hello", "world", "", ""}, + }; + + for (const char &c : " \t\r\n") { + auto res = cli.Put("/put", {}, items, string("abc123").append(1, c)); + ASSERT_EQ(Error::UnsupportedMultipartBoundaryChars, res.error()); + ASSERT_FALSE(res); + } +} + +TEST(MultipartFormDataTest, AlternateFilename) { + auto handled = false; + + Server svr; + svr.Post("/test", [&](const Request &req, Response &res) { + ASSERT_EQ(3u, req.files.size()); + + auto it = req.files.begin(); + ASSERT_EQ("file1", it->second.name); + ASSERT_EQ("A.txt", it->second.filename); + ASSERT_EQ("text/plain", it->second.content_type); + ASSERT_EQ("Content of a.txt.\r\n", it->second.content); + + ++it; + ASSERT_EQ("file2", it->second.name); + ASSERT_EQ("a.html", it->second.filename); + ASSERT_EQ("text/html", it->second.content_type); + ASSERT_EQ("Content of a.html.\r\n", + it->second.content); + + ++it; + ASSERT_EQ("text", it->second.name); + ASSERT_EQ("", it->second.filename); + ASSERT_EQ("", it->second.content_type); + ASSERT_EQ("text default", it->second.content); + + res.set_content("ok", "text/plain"); + + handled = true; + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + ASSERT_TRUE(handled); + }); + + svr.wait_until_ready(); + + auto req = "POST /test HTTP/1.1\r\n" + "Content-Type: multipart/form-data;boundary=--------\r\n" + "Content-Length: 399\r\n" + "\r\n" + "----------\r\n" + "Content-Disposition: form-data; name=\"text\"\r\n" + "\r\n" + "text default\r\n" + "----------\r\n" + "Content-Disposition: form-data; filename*=\"UTF-8''%41.txt\"; " + "filename=\"a.txt\"; name=\"file1\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "Content of a.txt.\r\n" + "\r\n" + "----------\r\n" + "Content-Disposition: form-data; name=\"file2\" ;filename = " + "\"a.html\"\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "Content of a.html.\r\n" + "\r\n" + "------------\r\n"; + + ASSERT_TRUE(send_request(1, req)); +} + +TEST(MultipartFormDataTest, CloseDelimiterWithoutCRLF) { + auto handled = false; + + Server svr; + svr.Post("/test", [&](const Request &req, Response &) { + ASSERT_EQ(2u, req.files.size()); + + auto it = req.files.begin(); + ASSERT_EQ("text1", it->second.name); + ASSERT_EQ("text1", it->second.content); + + ++it; + ASSERT_EQ("text2", it->second.name); + ASSERT_EQ("text2", it->second.content); + + handled = true; + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + ASSERT_TRUE(handled); + }); + + svr.wait_until_ready(); + + auto req = "POST /test HTTP/1.1\r\n" + "Content-Type: multipart/form-data;boundary=--------\r\n" + "Content-Length: 146\r\n" + "\r\n----------\r\n" + "Content-Disposition: form-data; name=\"text1\"\r\n" + "\r\n" + "text1" + "\r\n----------\r\n" + "Content-Disposition: form-data; name=\"text2\"\r\n" + "\r\n" + "text2" + "\r\n------------"; + + std::string resonse; + ASSERT_TRUE(send_request(1, req, &resonse)); + ASSERT_EQ("200", resonse.substr(9, 3)); +} + +TEST(MultipartFormDataTest, ContentLength) { + auto handled = false; + + Server svr; + svr.Post("/test", [&](const Request &req, Response &) { + ASSERT_EQ(2u, req.files.size()); + + auto it = req.files.begin(); + ASSERT_EQ("text1", it->second.name); + ASSERT_EQ("text1", it->second.content); + + ++it; + ASSERT_EQ("text2", it->second.name); + ASSERT_EQ("text2", it->second.content); + + handled = true; + }); + + thread t = thread([&] { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + ASSERT_TRUE(handled); + }); + + svr.wait_until_ready(); + + auto req = "POST /test HTTP/1.1\r\n" + "Content-Type: multipart/form-data;boundary=--------\r\n" + "Content-Length: 167\r\n" + "\r\n----------\r\n" + "Content-Disposition: form-data; name=\"text1\"\r\n" + "Content-Length: 5\r\n" + "\r\n" + "text1" + "\r\n----------\r\n" + "Content-Disposition: form-data; name=\"text2\"\r\n" + "\r\n" + "text2" + "\r\n------------\r\n"; + + std::string resonse; + ASSERT_TRUE(send_request(1, req, &resonse)); + ASSERT_EQ("200", resonse.substr(9, 3)); +} + +#endif + +#ifndef _WIN32 +class UnixSocketTest : public ::testing::Test { +protected: + void TearDown() override { std::remove(pathname_.c_str()); } + + void client_GET(const std::string &addr) { + httplib::Client cli{addr}; + cli.set_address_family(AF_UNIX); + ASSERT_TRUE(cli.is_valid()); + + const auto &result = cli.Get(pattern_); + ASSERT_TRUE(result) << "error: " << result.error(); + + const auto &resp = result.value(); + EXPECT_EQ(resp.status, StatusCode::OK_200); + EXPECT_EQ(resp.body, content_); + } + + const std::string pathname_{"./httplib-server.sock"}; + const std::string pattern_{"/hi"}; + const std::string content_{"Hello World!"}; +}; + +TEST_F(UnixSocketTest, pathname) { + httplib::Server svr; + svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) { + res.set_content(content_, "text/plain"); + }); + + std::thread t{[&] { + ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80)); + }}; + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + ASSERT_TRUE(svr.is_running()); + + client_GET(pathname_); +} + +#if defined(__linux__) || \ + /* __APPLE__ */ (defined(SOL_LOCAL) && defined(SO_PEERPID)) +TEST_F(UnixSocketTest, PeerPid) { + httplib::Server svr; + std::string remote_port_val; + svr.Get(pattern_, [&](const httplib::Request &req, httplib::Response &res) { + res.set_content(content_, "text/plain"); + remote_port_val = req.get_header_value("REMOTE_PORT"); + }); + + std::thread t{[&] { + ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80)); + }}; + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + ASSERT_TRUE(svr.is_running()); + + client_GET(pathname_); + EXPECT_EQ(std::to_string(getpid()), remote_port_val); +} +#endif + +#ifdef __linux__ +TEST_F(UnixSocketTest, abstract) { + constexpr char svr_path[]{"\x00httplib-server.sock"}; + const std::string abstract_addr{svr_path, sizeof(svr_path) - 1}; + + httplib::Server svr; + svr.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) { + res.set_content(content_, "text/plain"); + }); + + std::thread t{[&] { + ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(abstract_addr, 80)); + }}; + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + ASSERT_TRUE(svr.is_running()); + + client_GET(abstract_addr); +} +#endif + +TEST(SocketStream, is_writable_UNIX) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, fds)); + + const auto asSocketStream = [&](socket_t fd, + std::function func) { + return detail::process_client_socket(fd, 0, 0, 0, 0, func); + }; + asSocketStream(fds[0], [&](Stream &s0) { + EXPECT_EQ(s0.socket(), fds[0]); + EXPECT_TRUE(s0.is_writable()); + + EXPECT_EQ(0, close(fds[1])); + EXPECT_FALSE(s0.is_writable()); + + return true; + }); + EXPECT_EQ(0, close(fds[0])); +} + +TEST(SocketStream, is_writable_INET) { + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(PORT + 1); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + int disconnected_svr_sock = -1; + std::thread svr{[&] { + const int s = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LE(0, s); + ASSERT_EQ(0, ::bind(s, reinterpret_cast(&addr), sizeof(addr))); + ASSERT_EQ(0, listen(s, 1)); + ASSERT_LE(0, disconnected_svr_sock = accept(s, nullptr, nullptr)); + ASSERT_EQ(0, close(s)); + }}; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::thread cli{[&] { + const int s = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LE(0, s); + ASSERT_EQ(0, connect(s, reinterpret_cast(&addr), sizeof(addr))); + ASSERT_EQ(0, close(s)); + }}; + cli.join(); + svr.join(); + ASSERT_NE(disconnected_svr_sock, -1); + + const auto asSocketStream = [&](socket_t fd, + std::function func) { + return detail::process_client_socket(fd, 0, 0, 0, 0, func); + }; + asSocketStream(disconnected_svr_sock, [&](Stream &ss) { + EXPECT_EQ(ss.socket(), disconnected_svr_sock); + EXPECT_FALSE(ss.is_writable()); + + return true; + }); + + ASSERT_EQ(0, close(disconnected_svr_sock)); +} +#endif // #ifndef _WIN32 + +TEST(TaskQueueTest, IncreaseAtomicInteger) { + static constexpr unsigned int number_of_tasks{1000000}; + std::atomic_uint count{0}; + std::unique_ptr task_queue{ + new ThreadPool{CPPHTTPLIB_THREAD_POOL_COUNT}}; + + for (unsigned int i = 0; i < number_of_tasks; ++i) { + auto queued = task_queue->enqueue( + [&count] { count.fetch_add(1, std::memory_order_relaxed); }); + EXPECT_TRUE(queued); + } + + EXPECT_NO_THROW(task_queue->shutdown()); + EXPECT_EQ(number_of_tasks, count.load()); +} + +TEST(TaskQueueTest, IncreaseAtomicIntegerWithQueueLimit) { + static constexpr unsigned int number_of_tasks{1000000}; + static constexpr unsigned int qlimit{2}; + unsigned int queued_count{0}; + std::atomic_uint count{0}; + std::unique_ptr task_queue{ + new ThreadPool{/*num_threads=*/1, qlimit}}; + + for (unsigned int i = 0; i < number_of_tasks; ++i) { + if (task_queue->enqueue( + [&count] { count.fetch_add(1, std::memory_order_relaxed); })) { + queued_count++; + } + } + + EXPECT_NO_THROW(task_queue->shutdown()); + EXPECT_EQ(queued_count, count.load()); + EXPECT_TRUE(queued_count <= number_of_tasks); + EXPECT_TRUE(queued_count >= qlimit); +} + +TEST(TaskQueueTest, MaxQueuedRequests) { + static constexpr unsigned int qlimit{3}; + std::unique_ptr task_queue{new ThreadPool{1, qlimit}}; + std::condition_variable sem_cv; + std::mutex sem_mtx; + int credits = 0; + bool queued; + + /* Fill up the queue with tasks that will block until we give them credits to + * complete. */ + for (unsigned int n = 0; n <= qlimit;) { + queued = task_queue->enqueue([&sem_mtx, &sem_cv, &credits] { + std::unique_lock lock(sem_mtx); + while (credits <= 0) { + sem_cv.wait(lock); + } + /* Consume the credit and signal the test code if they are all gone. */ + if (--credits == 0) { sem_cv.notify_one(); } + }); + + if (n < qlimit) { + /* The first qlimit enqueues must succeed. */ + EXPECT_TRUE(queued); + } else { + /* The last one will succeed only when the worker thread + * starts and dequeues the first blocking task. Although + * not necessary for the correctness of this test, we sleep for + * a short while to avoid busy waiting. */ + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + if (queued) { n++; } + } + + /* Further enqueues must fail since the queue is full. */ + for (auto i = 0; i < 4; i++) { + queued = task_queue->enqueue([] {}); + EXPECT_FALSE(queued); + } + + /* Give the credits to allow the previous tasks to complete. */ + { + std::unique_lock lock(sem_mtx); + credits += qlimit + 1; + } + sem_cv.notify_all(); + + /* Wait for all the credits to be consumed. */ + { + std::unique_lock lock(sem_mtx); + while (credits > 0) { + sem_cv.wait(lock); + } + } + + /* Check that we are able again to enqueue at least qlimit tasks. */ + for (unsigned int i = 0; i < qlimit; i++) { + queued = task_queue->enqueue([] {}); + EXPECT_TRUE(queued); + } + + EXPECT_NO_THROW(task_queue->shutdown()); +} + +TEST(RedirectTest, RedirectToUrlWithQueryParameters) { + Server svr; + + svr.Get("/", [](const Request & /*req*/, Response &res) { + res.set_redirect(R"(/hello?key=val%26key2%3Dval2)"); + }); + + svr.Get("/hello", [](const Request &req, Response &res) { + res.set_content(req.get_param_value("key"), "text/plain"); + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + cli.set_follow_location(true); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("val&key2=val2", res->body); + } +} + +TEST(VulnerabilityTest, CRLFInjection) { + Server svr; + + svr.Post("/test1", [](const Request & /*req*/, Response &res) { + res.set_content("Hello 1", "text/plain"); + }); + + svr.Delete("/test2", [](const Request & /*req*/, Response &res) { + res.set_content("Hello 2", "text/plain"); + }); + + svr.Put("/test3", [](const Request & /*req*/, Response &res) { + res.set_content("Hello 3", "text/plain"); + }); + + svr.Patch("/test4", [](const Request & /*req*/, Response &res) { + res.set_content("Hello 4", "text/plain"); + }); + + svr.set_logger([](const Request &req, const Response & /*res*/) { + for (const auto &x : req.headers) { + auto key = x.first; + EXPECT_STRNE("evil", key.c_str()); + } + }); + + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + { + Client cli(HOST, PORT); + + cli.Post("/test1", "A=B", + "application/x-www-form-urlencoded\r\nevil: hello1"); + cli.Delete("/test2", "A=B", "text/plain\r\nevil: hello2"); + cli.Put("/test3", "text", "text/plain\r\nevil: hello3"); + cli.Patch("/test4", "content", "text/plain\r\nevil: hello4"); + } +} + +TEST(PathParamsTest, StaticMatch) { + const auto pattern = "/users/all"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/all"; + ASSERT_TRUE(matcher.match(request)); + + std::unordered_map expected_params = {}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(PathParamsTest, StaticMismatch) { + const auto pattern = "/users/all"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/1"; + ASSERT_FALSE(matcher.match(request)); +} + +TEST(PathParamsTest, SingleParamInTheMiddle) { + const auto pattern = "/users/:id/subscriptions"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/42/subscriptions"; + ASSERT_TRUE(matcher.match(request)); + + std::unordered_map expected_params = {{"id", "42"}}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(PathParamsTest, SingleParamInTheEnd) { + const auto pattern = "/users/:id"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/24"; + ASSERT_TRUE(matcher.match(request)); + + std::unordered_map expected_params = {{"id", "24"}}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(PathParamsTest, SingleParamInTheEndTrailingSlash) { + const auto pattern = "/users/:id/"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/42/"; + ASSERT_TRUE(matcher.match(request)); + std::unordered_map expected_params = {{"id", "42"}}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(PathParamsTest, EmptyParam) { + const auto pattern = "/users/:id/"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users//"; + ASSERT_TRUE(matcher.match(request)); + + std::unordered_map expected_params = {{"id", ""}}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(PathParamsTest, FragmentMismatch) { + const auto pattern = "/users/:id/"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/admins/24/"; + ASSERT_FALSE(matcher.match(request)); +} + +TEST(PathParamsTest, ExtraFragments) { + const auto pattern = "/users/:id"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/42/subscriptions"; + ASSERT_FALSE(matcher.match(request)); +} + +TEST(PathParamsTest, MissingTrailingParam) { + const auto pattern = "/users/:id"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users"; + ASSERT_FALSE(matcher.match(request)); +} + +TEST(PathParamsTest, MissingParamInTheMiddle) { + const auto pattern = "/users/:id/subscriptions"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/subscriptions"; + ASSERT_FALSE(matcher.match(request)); +} + +TEST(PathParamsTest, MultipleParams) { + const auto pattern = "/users/:userid/subscriptions/:subid"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/users/42/subscriptions/2"; + ASSERT_TRUE(matcher.match(request)); + + std::unordered_map expected_params = { + {"userid", "42"}, {"subid", "2"}}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(PathParamsTest, SequenceOfParams) { + const auto pattern = "/values/:x/:y/:z"; + detail::PathParamsMatcher matcher(pattern); + + Request request; + request.path = "/values/1/2/3"; + ASSERT_TRUE(matcher.match(request)); + + std::unordered_map expected_params = { + {"x", "1"}, {"y", "2"}, {"z", "3"}}; + + EXPECT_EQ(request.path_params, expected_params); +} + +TEST(UniversalClientImplTest, Ipv6LiteralAddress) { + // If ipv6 regex working, regex match codepath is taken. + // else port will default to 80 in Client impl + int clientImplMagicPort = 80; + int port = 4321; + // above ports must be different to avoid false negative + EXPECT_NE(clientImplMagicPort, port); + + std::string ipV6TestURL = "http://[ff06::c3]"; + + Client cli(ipV6TestURL + ":" + std::to_string(port), CLIENT_CERT_FILE, + CLIENT_PRIVATE_KEY_FILE); + EXPECT_EQ(cli.port(), port); +} diff --git a/test/test.conf b/test/test.conf new file mode 100644 index 0000000000000000000000000000000000000000..1cf7d63961bc096c614da8ca3fcf7cbca3e94e03 --- /dev/null +++ b/test/test.conf @@ -0,0 +1,21 @@ +[req] +default_bits = 2048 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +output_password = mypass + +[req_distinguished_name] +C = US +ST = Test State or Province +L = Test Locality +O = Organization Name +OU = Organizational Unit Name +CN = Common Name +emailAddress = test@email.address + +[req_attributes] +challengePassword = 1234 + +[SAN] +subjectAltName=IP:127.0.0.1 diff --git a/test/test.rootCA.conf b/test/test.rootCA.conf new file mode 100644 index 0000000000000000000000000000000000000000..9d7037dd949c1e5cdd80128273470a64e0138126 --- /dev/null +++ b/test/test.rootCA.conf @@ -0,0 +1,18 @@ +[req] +default_bits = 2048 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +output_password = mypass + +[req_distinguished_name] +C = US +ST = Test State or Province +L = Test Locality +O = Organization Name +OU = Organizational Unit Name +CN = Root CA Name +emailAddress = test@email.address + +[req_attributes] +challengePassword = 1234 diff --git a/test/test.sln b/test/test.sln new file mode 100644 index 0000000000000000000000000000000000000000..8377dd7f6eaa08cccc6e12276427bb26fbc24454 --- /dev/null +++ b/test/test.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.20617.1 PREVIEW +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|Win32.ActiveCfg = Debug|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|Win32.Build.0 = Debug|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|x64.ActiveCfg = Debug|x64 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|x64.Build.0 = Debug|x64 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|Win32.ActiveCfg = Release|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|Win32.Build.0 = Release|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|x64.ActiveCfg = Release|x64 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/test.vcxproj b/test/test.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..b169311b1b6091f3b3a2c57567cfd27c996298f7 --- /dev/null +++ b/test/test.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26} + Win32Proj + test + 10.0 + + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + true + $(IncludePath) + $(LibraryPath) + + + true + $(IncludePath) + $(LibraryPath) + + + false + $(IncludePath) + $(LibraryPath) + + + false + $(IncludePath) + $(LibraryPath) + + + + + + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + true + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + true + /bigobj %(AdditionalOptions) + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + true + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + true + /bigobj %(AdditionalOptions) + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + diff --git a/test/test_proxy.cc b/test/test_proxy.cc new file mode 100644 index 0000000000000000000000000000000000000000..88a8ba963d3b4aaa2cf9478845992c214d338b87 --- /dev/null +++ b/test/test_proxy.cc @@ -0,0 +1,288 @@ +#include +#include +#include + +using namespace std; +using namespace httplib; + +template +void ProxyTest(T& cli, bool basic) { + cli.set_proxy("localhost", basic ? 3128 : 3129); + auto res = cli.Get("/httpbin/get"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::ProxyAuthenticationRequired_407, res->status); +} + +TEST(ProxyTest, NoSSLBasic) { + Client cli("nghttp2.org"); + ProxyTest(cli, true); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(ProxyTest, SSLBasic) { + SSLClient cli("nghttp2.org"); + ProxyTest(cli, true); +} + +TEST(ProxyTest, NoSSLDigest) { + Client cli("nghttp2.org"); + ProxyTest(cli, false); +} + +TEST(ProxyTest, SSLDigest) { + SSLClient cli("nghttp2.org"); + ProxyTest(cli, false); +} +#endif + +// ---------------------------------------------------------------------------- + +template +void RedirectProxyText(T& cli, const char *path, bool basic) { + cli.set_proxy("localhost", basic ? 3128 : 3129); + if (basic) { + cli.set_proxy_basic_auth("hello", "world"); + } else { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli.set_proxy_digest_auth("hello", "world"); +#endif + } + cli.set_follow_location(true); + + auto res = cli.Get(path); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::OK_200, res->status); +} + +TEST(RedirectTest, HTTPBinNoSSLBasic) { + Client cli("nghttp2.org"); + RedirectProxyText(cli, "/httpbin/redirect/2", true); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(RedirectTest, HTTPBinNoSSLDigest) { + Client cli("nghttp2.org"); + RedirectProxyText(cli, "/httpbin/redirect/2", false); +} + +TEST(RedirectTest, HTTPBinSSLBasic) { + SSLClient cli("nghttp2.org"); + RedirectProxyText(cli, "/httpbin/redirect/2", true); +} + +TEST(RedirectTest, HTTPBinSSLDigest) { + SSLClient cli("nghttp2.org"); + RedirectProxyText(cli, "/httpbin/redirect/2", false); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(RedirectTest, YouTubeNoSSLBasic) { + Client cli("youtube.com"); + RedirectProxyText(cli, "/", true); +} + +TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) { + Client cli("youtube.com"); + RedirectProxyText(cli, "/", false); +} + +TEST(RedirectTest, YouTubeSSLBasic) { + SSLClient cli("youtube.com"); + RedirectProxyText(cli, "/", true); +} + +TEST(RedirectTest, YouTubeSSLDigest) { + SSLClient cli("youtube.com"); + RedirectProxyText(cli, "/", false); +} +#endif + +// ---------------------------------------------------------------------------- + +template +void BaseAuthTestFromHTTPWatch(T& cli) { + cli.set_proxy("localhost", 3128); + cli.set_proxy_basic_auth("hello", "world"); + + { + auto res = cli.Get("/basic-auth/hello/world"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + { + auto res = + cli.Get("/basic-auth/hello/world", + {make_basic_authentication_header("hello", "world")}); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + cli.set_basic_auth("hello", "world"); + auto res = cli.Get("/basic-auth/hello/world"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + cli.set_basic_auth("hello", "bad"); + auto res = cli.Get("/basic-auth/hello/world"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + { + cli.set_basic_auth("bad", "world"); + auto res = cli.Get("/basic-auth/hello/world"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } +} + +TEST(BaseAuthTest, NoSSL) { + Client cli("httpbin.org"); + BaseAuthTestFromHTTPWatch(cli); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(BaseAuthTest, SSL) { + SSLClient cli("httpbin.org"); + BaseAuthTestFromHTTPWatch(cli); +} +#endif + +// ---------------------------------------------------------------------------- + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +template +void DigestAuthTestFromHTTPWatch(T& cli) { + cli.set_proxy("localhost", 3129); + cli.set_proxy_digest_auth("hello", "world"); + + { + auto res = cli.Get("/digest-auth/auth/hello/world"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + { + std::vector paths = { + "/digest-auth/auth/hello/world/MD5", + "/digest-auth/auth/hello/world/SHA-256", + "/digest-auth/auth/hello/world/SHA-512", + "/digest-auth/auth-int/hello/world/MD5", + }; + + cli.set_digest_auth("hello", "world"); + for (auto path : paths) { + auto res = cli.Get(path.c_str()); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + cli.set_digest_auth("hello", "bad"); + for (auto path : paths) { + auto res = cli.Get(path.c_str()); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + } + + // NOTE: Until httpbin.org fixes issue #46, the following test is commented + // out. Please see https://httpbin.org/digest-auth/auth/hello/world + // cli.set_digest_auth("bad", "world"); + // for (auto path : paths) { + // auto res = cli.Get(path.c_str()); + // ASSERT_TRUE(res != nullptr); + // EXPECT_EQ(StatusCode::Unauthorized_401, res->status); + // } + } +} + +TEST(DigestAuthTest, SSL) { + SSLClient cli("httpbin.org"); + DigestAuthTestFromHTTPWatch(cli); +} + +TEST(DigestAuthTest, NoSSL) { + Client cli("httpbin.org"); + DigestAuthTestFromHTTPWatch(cli); +} +#endif + +// ---------------------------------------------------------------------------- + +template +void KeepAliveTest(T& cli, bool basic) { + cli.set_proxy("localhost", basic ? 3128 : 3129); + if (basic) { + cli.set_proxy_basic_auth("hello", "world"); + } else { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli.set_proxy_digest_auth("hello", "world"); +#endif + } + + cli.set_follow_location(true); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli.set_digest_auth("hello", "world"); +#endif + + { + auto res = cli.Get("/httpbin/get"); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + { + auto res = cli.Get("/httpbin/redirect/2"); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + + { + std::vector paths = { + "/httpbin/digest-auth/auth/hello/world/MD5", + "/httpbin/digest-auth/auth/hello/world/SHA-256", + "/httpbin/digest-auth/auth/hello/world/SHA-512", + "/httpbin/digest-auth/auth-int/hello/world/MD5", + }; + + for (auto path: paths) { + auto res = cli.Get(path.c_str()); + EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + } + + { + int count = 10; + while (count--) { + auto res = cli.Get("/httpbin/get"); + EXPECT_EQ(StatusCode::OK_200, res->status); + } + } +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +TEST(KeepAliveTest, NoSSLWithBasic) { + Client cli("nghttp2.org"); + KeepAliveTest(cli, true); +} + +TEST(KeepAliveTest, SSLWithBasic) { + SSLClient cli("nghttp2.org"); + KeepAliveTest(cli, true); +} + +TEST(KeepAliveTest, NoSSLWithDigest) { + Client cli("nghttp2.org"); + KeepAliveTest(cli, false); +} + +TEST(KeepAliveTest, SSLWithDigest) { + SSLClient cli("nghttp2.org"); + KeepAliveTest(cli, false); +} +#endif diff --git a/test/www/dir/1MB.txt b/test/www/dir/1MB.txt new file mode 100644 index 0000000000000000000000000000000000000000..f5a9c204fe4969f7bfa16f822744265dd386f9c4 --- /dev/null +++ b/test/www/dir/1MB.txt @@ -0,0 +1,8192 @@ +9254835974458887629672873635789957411886024698554157393849494864228024962939550688297074527198420261051675205999609689838587412 +7948702662533481896767559573369920938242346354580061545409242090168773727371802699309443935396635866263937828773324526334321892 +7929250312741837331511829643632683169694074912332726993582394725302853411901337696207186358524323117172520907433878952968176465 +9486937364148093931718552300016332142708943190856638524388888569011747617956915519539025796115901484762122047712200094207683584 +0703675740855407318047361595661595146837376373951978537785605481083388906490085533348547865459237835407372374738389274773789264 +3524314516560200536698529022539598732463389124803873184044464663165630452635665559603483233341839268186056673186867104904449866 +3388466377320953222057779182433549144340237502432464295061371141084500222833875925546082542869030852833895137466510262849050187 +2359980877010447170873386178573828860442255448874794721230413368694441497441338856684036949118353204002591974711928301953002372 +6372613557152801003023836434406997863096739346637849381675791163293956729985652182807911615231198696051411047800847064484769940 +4555399892730282159333189930818248844009737851281434494736943722577947684829017806688345340403991974811107725657399196027995602 +3976607710681047393460981746843991626375149576917517874980586155555711990727221948467045177979554215577477183785345135535682148 +8981224019853812859833307577420310576654838103212699667682513798088777109119729024038590682109547975734687407898816774007038892 +9017123764104410282402525387418186790830943654104676998756681754083664849203556615995024922746652778319099999725942494552746687 +4602993085345245279681119328539229356959910969065035141679707233267342542524482900839390718291823532073464112692162110160887576 +8646564120617234541914353449310981315044240998894442066188871011899825887695202156119273906958004776559173081177243291448706570 +4866033151494706872899672197703352421058356661105030485577210664650513250767511742264039882291373723618030276740280451621184372 +9438899492601727676139376045138079061355198567322008270344297244790265795845160675322685239683610065522225253070076549915962270 +8389935622999431104891971065250599273560270041530059453015141663393829191008304620453278274234791447194116642959854046669653221 +4390066870930359126190473237338490788280116222078414392005864878004044572924012489353108185972058375678318527434060575546457283 +0571024164252835918003185534087097269093594036226831977994875139354124615791501602629221816855731144878875700135493432353006251 +5927821910139360288795697248742701824779517711136895716699422565064551330393130338804982871980774254446227613970394730940420871 +3742377272014443550088749485134437400845098911378170974616143540710504172620523980130687874417124218036934374655684373965565750 +1478423915766121994159683752043056020433068572256778826939436280481378432842397489866548450027752963017529841096517341127140258 +8566764493817154363962092792522076892737665735147823735202597353071978920287229637466345140359899735477359400126088124931881468 +8940255760337363163978793913265843793885760290540433771518954295210648595347128727773745692808704267887587662204824695198843990 +0899757006576147578169404543275069681042994730814241204020586377450354179962282687398746645565905502250948590090052580759060254 +6486974981051360582508378437652320531515639324364424491392656403886587101611394285202791775180181840494403192370521504242888310 +0832863857962097233772304981176182241414796661234070317886749751979535621508014316193777356696545211120669034308685700669047575 +1601920560631092775469657814901382577801938745924198963515833985856042317888652481510158158143597978988591350058772254540887509 +2132235148831014717469033542114409228818750252221985002159849994061876844567039235968616101425307518401162305005282076459564485 +3749005491254116003221964938075605607977980187695543266859111481060871108661145469674590183816636520815523589998805144183387552 +3454557776795930937812464849331806246436437524220402821725671887519345824588678830784061509096255092713692879753496348543679698 +6604711892156464428503873379368266697844691398807542828869396127474509499861163107833096454590997165019449027223124752455672795 +1464056065563451406292025100441586772081609300298880665395988771146415490692648836974652685280576941091682396423173897267646839 +0355383165630525414486907132932207574564820206814521900700480593494653392345246480522618317569197186894847097184780188176595384 +2234714652935409724403164799544553996270776652429105659472464101488105672623286713328942045106946821859926624587221567145826782 +0309935578008890373154226142294034025862773115052182464935233315361839771441799453095926400038395389556682588458216003237653863 +1061156071932484650516913321439215405043703716040614962270232640142284681169472791577978618224769535957766957086206741391709970 +7647351716205014294072195135184469100518783539374945598848168573993875333505334011212625117442647232720744618603822330582078365 +6190042304849971211815384939288709598033150233044255711655989057889996862762413082297341687788607689416591830790638752880761817 +3350993967482666602170935703513835999165561197161777938477157925978429670778105685120890113130859239045083522459241247368307415 +5460081669964922086720150722963115784946235169601550836667474277946931369187925788894576312382075479577915204545165252523196796 +9483728931707116749644793319165741326903913011722074179317419072713429472369240811255731316334667969479890293999075667048649131 +3479265891282260862086967904220845208360231245372151218473448305464967324382299780822943482467600298687513476936031155077963601 +5358607166888753271639287331221882667306965170073890751994864930247242443182503197751744519602399141153643026523167858853130538 +7454234112327473005509721991333469328638161495819890863131852055720398755713433050343878076412540696773945682790587599473898772 +1829500286670935139918198012253669665196719074234685642111242009658467409729732638466770339415767564217863874440642300491597266 +5585497029761221358413402963546931530898077983556347511671398790492531247609197801647309393017719703556474420667077635431137035 +8446511749461154540070308470454488783535318141900958816289412388611370858697615498746416636102261400237788596935028958347787028 +3125589335242227429164632721324285385335478344437654660712476162463759322956724441170491898597020350367163787012219584863841888 +6802337989299969519242898184367047999251583706804577726542893830886334416591927545136810611568115854449708934129157129519681281 +4502536799805954583144549032621959969753071050956941684132891200046324576389173182566153807220810192479638581311891427849367417 +6325541021938064574977797529368876421571303611445571554655335842928934553745122110842178236045825160364424732178843094315127909 +7416551206319999579172591987511489686313571898121634456963111089610744020435972487389528243640299599133169203384400119064268257 +5942558924368052900576494867848083529988242331335385049200017447913990221665638834941940132033114280719741451021709322013495656 +3034267296680277379429583962288582457402893052674209528241394670330734634693333770067544918231098413897817493181756172191761769 +4462568735945386723562623780129364617626340957792264088426814103842409805756856047587770203362285297182877719759239486173167134 +8181657858983878076788365457679611406051878073922935400724071002626732091440173091689286855430601190105542880854867957669944342 +1682764258327911438109776589215105175058720345720016147450773913501679216877023673201387960875767306750352156509386560915532694 +4168453046768399507263930483191895109159119018724369742162771101927944398681984030667327651300421681200981291741743361136642792 +5961214949092908589726506803431211408890378633711553535211419472586275853572210445315467622414789208830684013971300686277675328 +9269710543143964341467182430089608998710751826481978504336310277969086698368677287182389959721242273938854619109417313330166529 +8486459999966843090378171773263091325696386996545517566407679424699279968298703891528397394384066258625248890398189881141241156 +1449849546688513154852662654151484110306993368687380709636409815697889872688403677017676988174939843342948688833489247136945501 +3026821425662283243215241228426396875362999466310497066906176240972976192127107346129568606866349374029240104708104443230866441 +4888599832610923002904596161762304764008702325429097763470366526898011656507069583771589908698710318907474941536590688671464002 +2187786798670885931014137550230451723811271617853032443909991254363190383287294838940424051082728434647528986018234698035977262 +4010150476565439429491060857277549534561616385054516756344772776300804281913985317098584407945908254666687113999982299034911916 +9543106789274635042741853569705907409683466986445551664737067041728196021666244823741197574167419992584104093228192902092720801 +1102588771206288521669854794170734776396759389699841902844928003578465247527168100807066013305698109312645455547945036883046986 +3363256734764708042924448420459037705906529627671525144633382899415555101340497992824046104327698365969273385304329013580634534 +5814024400502542484138080303093675479908925878015957334513036393579080542663814740530799226052633254072603206379019404756963615 +8568459878266889844587337958272179244147420281407959235700418194754222652142770182396574372220705526302797889629403089542338729 +8226364273353855713782831639652716359809735693921459950536413341806490125092787610897542413754704628839004798504470781343226879 +9564108089806303912196187245286576464060313163695650258929860024346682416259472890719900060340274063102211811727469052257146328 +0723807096388817245239864356681843226170736144832701238842559718295427226555369395998370081796589238415142792914737246512392617 +4241424816471283201248965000004171003042756134627995493041482479770591013182623840449510466593158465328399081296605282376713554 +9973513220590332519460381924338347505856779256581376567327469745338170108702340101128824216183825500136464452346025348049667485 +8383897971629124447860057453350337067099519586684049058435585462779736095630666963739484221218567448740285624710665297561214099 +9964116268591382706460872763874878534297946361217165257274977145088020502651470013064060961213429959898581048432243027406481577 +4824315082578790346775943088343852549806522479147355182415674805204204673847884219703487656256651056490611337757236524094938348 +7062382447597254598729362880233858271292436951748099256624309342834053225940366427538355199347489811241328865453894894705029296 +9735693411211619628088603452483215272175364970550497151583596541206523461527332458408984429879086514102509879665768932131692226 +6961688387629152540756216546017259642317857065168508958573052496210655276891659756089714383999813015316305245290357709870563638 +3863034723721495784723589237391107262786771026496285596990386769945707595340965195448259401890929565886615037974745228156868323 +3287577904832967842610714371567414265151412481754216777139400930821306278453396427971928947503983582862250196984342590738386627 +1791128295449101351282899590128118669109794265058849951756999066974826947872800232214872176234126177766951584517353226892319930 +8805052099840613606036822579273082344123139982418321238213526119536401574939527633217151490658267375799590895845553195225375174 +1355928510541796297379079857922955866076933929404227257481153317061720856509437585235189609477388103725696248255380889259925943 +1402201875930840177564779603131963190398447445546273482826943610197406605496467698637182093544726028048446184407465904504733488 +1834037129267281181848805100644706716446934703366578946950288560270548606368029542736413168537166947783032181537827321728818305 +2399588088866368871684803227983829485917501647912319399696731866908470118289600881964063135888922021379521028829404789266368968 +5637489977535077950426899162426475829892339881113388204978816994197921201698140466319573566765752657389499556451516488232245559 +6112692169213605381302831305889438504096793315962284966459192757358537426473374519814657784899843476819443537896967951107338491 +7895505461382759098793722523795396339534176000429147383719128841803883949006205025948100090981495836498310874463726230844486437 +9522042614500829901321795525603265978871144309432521198633117362072879318547949126425751870262081563243482328537146079314556599 +5093542621599279420676140514142072884094585996707002095359240117940447064308608734332029368110977503867431654445743620282821742 +2074818355004815481495949283111185240895061522427508730372487670815127557063465146879286952537668866636765956480834805349247418 +0982616183770170277896101663961256388046096956303414645662042291995673037815635718874781061367093483701372208717092453548930045 +3197990341408426456860501773717322183556963874016603497159407408857792270849795326742052795163483613191779561704529979153717213 +0442381165605136382176204027769856937910886725474913822447353042136166362618403395513606165988761350028767809916274534709139581 +1546533500647539778292217072989397950692244341857345231008319214990106951110301762058019968223873956867021450221558460837271796 +1089583682950702039719953402978240768135379617758154644301927987331573523265837746123630494418924793748993674977892210713791382 +2010498095473532597719948269554932451554991652103201520706494302534512806373772862577948592827967992187111665796229241795272327 +4495947525239030494005205359163320911969546009892986032715554824732415447400580445227801191434206229755544175085033640445463730 +5799343664533857261737308836915890943267341995945607000250119768647338018370977116150224513412795224930773162810128318361091070 +2845165641335198012087900200223115487995666447653144922411381435396653532150893427712002942030941647409112450887593516206623257 +5995936189970345951128332927675055555031225056766626864377451946716279426300206796784320339236602175243416848009874418064376734 +2751388225971927284674719275737386426623723915170272496656834116124167598744380632642578314683658323664087930294748983479134152 +4463890675024338744201454760164930410895360686852249316115526602608580347281230566360947860956394767605509297995646286805155429 +3902535272296902694906404269870261097232828009262961508419387381919670436136425912908116240583462930917866834873246313946702767 +0171550221706917129701789274673785241823132172160837286626181116666263103300493781986590027327174868610919833041462749008446670 +1233446436949798212579086729206735034953949556045481891487880919889748751337486349345808929953562851746791383784762700811827813 +3273320169223863915019571833988285591541029709234409949795661766708317004888223115613777281294607726043777706813741640111212754 +2476840221289778122524588163601454015024966289209119530584971298419043747426874336591254749329149388984462650479516131162264547 +8321322112340265706034027413610131445579234122444443539849216325832257070066442940612976748450142222873158683339615189385385401 +2845301295555610107931163495080379964949955895954082606347174241430747973616106979000794362756653542557197433894470866065773098 +7002289110540946230118386265586199860767183813803128176222583812066464632835592487506968002253203796340562076570806978606547027 +0518614265711371822209002835579969441398099868205035333550662371029236766960492501859441092496004547436756442048667962205035734 +4662742623053069471722274771033535333247780680117662494001501801076484609461801093095512629224389725470873708072969419161134986 +8879423231727024904174405990021786946862967043559824446453539979244586561718968470449066018656109597862462444251052806647222165 +2462868109247823087920350263249330583062507248433983310105749602411182363633125913535849740456631462938827755069526554867046297 +0556317986368239387236732047607114173950737209007825151623731055762898522619516377121452352573813859619423710420648830645140602 +0693502619716624171936460417629499350461603844160041861287075322351538559343745415528939056080652464222483749575581089457335292 +4766904028429523888256867990946783862212788485169555738269492360138212802976905819698072914952002888319407814152289904290372830 +3063149485419553279766015944554584134585112055860455417890621924246024514671960271622075351843821409825893454426591239346175825 +4138071925467858356596514010874840756148724225545969356853427243328235466057330927837715365449101436095007553412409485033663473 +1718208277388066697955637942282188804022877425156297722332673824998500535568426990569492133060260476735245935911435663031104540 +7259896781146483425619092546253937377799155709431757305830799439617510543866635784180461435471514956432054478723110546701399601 +4212208190748800869765878332993087846354727233690197986460622890272309218632979045510485708917467310543076772888886969650697402 +5830555875156620379814707898244277917838097196097913596741388644704517369937389656755811401456911697429799824431768975340394565 +5218384019129708961023696421751573471619498755822698446106554346210228582416782419196987283985815988033363104751364948821321736 +1511408675805990625863697514171112598891753956197059919478438005392395089087719396999097527167894172833319566356364163919692543 +6760864061185222844062388472607262118669410742190580800609818458141651702885176634837319000673951554557807668749868767849900633 +4624693982782044541974663953840367719217355838087352148000020445830027606519679992919499646684217452676557562945022247829061975 +2885293059896283301736153794239325555898371313303885556968069349802848506212069388120502744705750493536808390340479824477084109 +4742565846910523167821445663540712005957374896244507720218360942226085109738527921622092099772083368812073433747682045449511884 +6164131243427554092783794384838205105688550227514505179310025715143470000166045228305059905780171994193190773288044579516190413 +6196746209214286300600159852801523856096528659384736317041341206058064233216653841144231397934288134905891354902685060365011048 +5888543435289034798476752259935453803961756168693460157400272434328483248778988626059542292033153708506682448981834226044225475 +3520105979519175798851693163134966793094384503981145911295918784323346445102768597120762535638265452619900970085082259502423098 +6023108133594199401032707688284772080067633191011227704160888217224741219472651134131857620995953537078628999627600457859691029 +7135355737374006279317910328897677569526320656280275576870747086734764513800879537763010892690268998504415888806085447064856828 +7067645951048993310751362481424429404370974532580270127897680271443323793244595619510531091473812713032955994538657114149409819 +4429631547566328842887647552139562050329710358137821590185444827892759275367380722401524911377817064992340751082835810723755618 +7949457580263990600187866192739809055130641391600646169338376355962933757309772135951086682561238922534649604316450790162767470 +6778696718174528127426896010544648198065903564137512038147927254912585276060875391066341248912566730098691603844487954144583289 +9567988530970013733492869819764508889770837509952069384446637448211216157716267753953316429209184585653933599673133926099458608 +3413492966631743395005504952764856687552734825362970605800032693220033767913422924066513158712050177343306017113090503548595910 +6782760373416377101401788671756172877579108677684185994928623883970442286211028728581037425407354811624955553337741266271223038 +4228769569080076083693265874897262718489495320015615486943453828550003614854151710616339504422293141400194809493593482338761720 +1275749976017493214306625272092330580872354604747422498275624636561377503079831465248194508227879655351358840776762816388383155 +4044611547062276323077587550347056464054608166550263311750875441257836393920920323092209759398994038142599127607649577377981910 +4521255521824521693676238300467676986666555950395922139641083951517880343036286546664953201729241686129223651162600610883998263 +4053431348518058012397818472045875672812077824208246071436792077005700411785602017912645840401853632467140385402691379589989961 +5296228207351306866595027867237475870719440724477597041700391443928732836085081541635185416372585087892465757549359102233343361 +5753047891155830333399279611743974102446066339908340811693445790222472707295688908858729491663915472040238337620182908302697924 +1508699849153464593476010736879669905272623521348097908417836104685217230447793112158735284635375208948973376371517941685224649 +0333224377446486130711894052961084313686306998844675785828086842465862992758130724435712277355911443550657464532883107599718794 +6835605634916383199967953082993703931572871914916373072477880100548805151438790628235173925034028725412169450381473208146789756 +5647596797800233744008641095874820885871714922036190240494729749620603436690380840330661337877272707920476284213555812599419547 +5513261332211064956118510684223202485323331553849116007081552356506786559518794150443263231676076605407451231256028775857402194 +0513233346964413426058663133294017428893643801135629939987380889256710071523164000264715556002787400893978524476668487881682249 +2238557041006601823583800792379249859430190860772025645499651325329407777431099926158122243379494002959281894688702064542524790 +5329597914788889055993063859757359411515855405549076867209561054324585069797183524577493085961561532791013967475565456058906425 +1727267651465421481105710991587303136944086691165624660300683125601416772368166283177444892976868345022274558919475820725390404 +9723590243421203442884921434798664645968065479222983928276913077586527562214486087189922112591555037425227530771384194025142882 +2491150360749458092334991769156796313818947278756674701926505201943095015129003490350920079450451251986198697962197375931585963 +3288323311745586996701741630221983319300390047571862205264349777484027201608059718085154305903308263735198400749807369566973087 +1513669962911297058622557295345513797998471626294474435337069062590410594233688469673875484253912558586263976338954760252676149 +5698941909248281308488717883921117273305117757034931046904729563167368375307728972002962702645828488935279252804415118514787991 +4226862654077359109954473957002792990601772994320272543965433892756039129932073592057677191275583708907760929447157089919000104 +5013015202975115790118348806423060224599193301855272198220793587287609415484559416236588999749109103840932649489845113026318936 +0096212754630289886604076288072412229982106747320501739034720205607227829189554712104511341545586461792092663001971581276075105 +1400830012525551115910295470153382076377587383792342868236429581401216573706230071139732339253271088253819425521718399148426972 +8130329129443141149308342972871751834898890054061408002066328617017958243761248655943163660366018684672224076482931610024551435 +1612470444029502611969134698674683976299405806211129021898957209691274647456212891723004609478869086828079731180673443147282673 +2207763612557030174078525394691981763423880246207823469307642881721011975478949627177448531469379452033830830547116487954748579 +8943550014526152971159374128630625318393391528677655228099671641521821415999666028671716937719971706453115362098995997004338463 +6633791090761145650463177053061879817237525424207069349153087084514135819202898699531517360951263097509259970351913296855368085 +3253556229437227815840782322932038842610282009077575504105765620857277275220518640524545781112378703944765595787111117088536490 +3612778048987899240807648365404713381232983899393777316948941796158037305227415310986733359795685540845368139757679718651388721 +0329656082303032301035960655149116897998069813522388783445067885544697734848160954106786926689147778838221235161040983098545023 +4949145925422482842946072091038603570358418034882170359973918018079551251028978485799644769497234033416966830413282850538365544 +9688715528548523732988559882524759509039740049260246259447423254924760566724656561201075170029641287177630144231062011355479091 +9326655743860681005816798142139776662356020296561383131825601205038606699757827396575776760705487871722516625005501108342115046 +4862493770761711413231246894538240278199950440029797832691068222332648670966923316771267216815856555063431665762362373752681261 +7759348913324715341816977430054410931106803880118579894939051307627827449748451051704548335839341424354678283555009009093334643 +5035504534504599464259968803145065603468459325562475588547886212145426779673200191888479107787688352706387961887120658923163596 +7064143605981003619608323632499268464142806140873807593464533126484595249138527994250140164063377300752448939069709418760740581 +3405460804222472492156715297971710492718005664789725902179902462523655008185987881883912400930038752420707768027434069222517179 +6201754337957471374021641947490208688904315801118730053700597398632018696041671232277781587402859428981324544384470959282694488 +6581501955752939978682011949909110724289175482558070539947143137365139752819830357605189104362596510417675677952395485738392629 +5415386438105335984223237131949793153812658394744575126207919072784736846230789712179591745207901814607314375532066326818332653 +3916132472388884065768704630042622537625667251509691545801542090771247404318802955070620862627476301969800427094218570764969521 +4673296443835065767694158631727161041961301871016507435883528289555722603890069782368970629525870136617529702747543702356107980 +3969736866659860369283152300636814307559050510391015595957669034065148689817184431746719346979216411505522453887905503375134993 +2033209673996359127998948182294581452192221728807476998740417776019537129643269360367751767407520936424856588589036363660084548 +8920359577043563701436228560312778025314413146903691717334802285060730546542075837699964573276987471415144914980259764316759773 +3627657012409360919929372833162804983136382545072275729679633045374976939117136265913209976809144657411039862524163682772934353 +7062667690768603813766580397495981633020198031283191319322060508875443418955017187814158494951274114076243932640382433951906100 +7089164714588749807180199878456232640504233132283953906601030144605887922753172274055475279148519962245834295034899100856266109 +6314588522281633268271129575999011547246790816567972537919142521521537511380072338334215014034228037819143744053883142669885047 +1533466041592069230562893355289214460798489775583299853132986367560533284715718053651634282346811547110422080041794519104713892 +4512970626101824923414591998781160477443695022763348614143799819070327746195782058463430126505518264262282025424239196981682678 +0616389983515682792619586855106396642118585620436668809283536133165065564894495800101984339481469054418070569566522216534870046 +3480404934288007401354060197932800798509063792079011642804930003377191431422069063134440047889535881274567178940747418743011151 +6923077833597121569133964233884887129884707102983051918568192593065049229148460460606709813558966649828440283419826988551789494 +7928629618912970441223379654942167876674509009329326197500704752789973531185610595644942798244043453568923441723724834770288101 +5686916225325175369639383018669733714398867749909229151589872339897763998251848983094518391124033198227341074940345064217565274 +6976813108342658294069521014494920511978448452850030848500379855545152314960261910035283177013574685072898109220517966532820914 +1235440485859974578507661524414185881273494203471328032423655298610228309217218895796957468358842309363210353318874365415441005 +4775757215064448647761305923181381380005564211124254441169985896444461359377488533806177746993363765363744834471832298666899659 +6549502546484198000053617666533373457701974326516846596770901748191355355442117405686714513545060992856554971022392931918431672 +7550521620433789157641377352042796699563142807992890238677899811145361588800069593324309664156547033893582449253982866608790396 +8335832703903786234598337454771069254394148676210696718885661839587950139009136903487221685179093872236368600881697661914186761 +6081423646547430855633909798819189360248786532594620983774708429259122445948885539867620755942089483824811916664871291112367965 +6763010129734054857751250818823088329080234337202140258490696882947562857997890963039131549439624749888135285626495741851799125 +3823814359862336359300824370629334246257087407174522366323576913384874982113357872199582879993086251942490130349296563220714459 +5201936494493880745125428463676510521663426683417020693977788479860674628237207703884747094987717754212968764024565059007572287 +5656800692578264688131164439239627529022443380780216659445516384845610830241501827976377913518196568744298647947111534785980646 +1465035810576162949068709350012676100595272189178329025511241633720229686245517256992739629981296918245960598907587127807526613 +3498649092457065189914681277177637039326561250640746889454833842625621406771442705555721525931725554548176228818511280348712812 +1526903631217370487596090586369687092431736602852047414002552907015226570166585529226164536775440508494117144839832365340311216 +7172800070700446830268660237981558246985408200103623671258540515214258800554645764441093416997150958224932045592907480540349584 +8145891984169127686107656905169532154735243264685794264058843466267191409417280492627793038318403119617408184518297221316412215 +3733585102819835157576236492205872180664881259553134950516961350305063296415930019908542524245430548400901559537444244647471710 +8919107817519795022023748231546623121376187758240479239499900553296636944913506176758776881811178925344276782657634277574411018 +0087408240115194690559689367771085179492160150159772172117583670772068640711181227707347785373406551286722418977845204918371862 +4087671496873156369239006050995430210075877655341012369255927938969111714405072240508066221092434986016434496634246773638497705 +3997764933956408083465951727938645280914517402927065500370134817839436837421504452664821784855528363909858322652715776945384928 +6358034140171396799781808478643680937510756584505346662094931360684566558016245219693809041873394197337373027746374913052226841 +3523525169184690915788330052438367441556380178099903982632705234888141866441214930597392851790874812046196446764134474357280196 +7912390044622498326943727559828557263667260605437809865126129166669886288145038860031004268429510310452514645934110806061259377 +8350389661397820495902304825219183306686077590455378114318515252557794852940943308375379300282537760761994418165168215848774199 +4335948571309322083199073879107610257555821153883090762370374143145191005083410872058977433580493717339726072170334542991808382 +0741068478360850873822704930167309345230781173492590875420659162725013692793497218747184011101436665668426379299561926131540226 +9684119382210299933488533355753954403421331470099036025961973755235128720357457186896140454721156015369426494838464841418224574 +2606550410151415944627360045368428365343042196701567415760981599314167562524281114850783881875311867997694821988388783550617647 +3906118902121264712371873625367711895188485445955724568729752600563482629569914020509331709891753567605376276818895453947988485 +1946035303735634882855442231649687139100528774921727985325887697840697252312164193587116219630593036960739462878472488751794288 +7553042934057050700594030020764349497635594011506182646429625749401961796662698874719407015328877341348471848266936805626731614 +3965083773308342246671432838925759649742042738645731254867213711401607696364783002153346490446057228458139827872431818981641456 +6290917282609648173484002432007182859707772380995362779354118181323028872604689080381882966273198128487451758287389558544864092 +5135412345654409193398015818933242323380089276961210614666090376540312839620184116491698184580875294639581135409937430385452967 +7814752714924949029739912516676992468879575093818117912716084850528773868618697654019734174399226706840162753982136880421595798 +8344230549785765490790122830425599688553302815508985004520791080241214227127540251939234623642895839337504147995870998311297157 +1312261536153577576475501063559792340822581967430971162124952202625290357772475691141555237352878643256411801046597748593662978 +5420113078044378326245454890074054926337548530109656935414953837202844128511917486560998089330806876653700112974245139455110655 +1446316293204894461498438056975498718142802987046646526458433588790266320884188848067627056955062757523352642280099730639892974 +8768410207973700878297712434460869178087366365740643368423050061014329802628323539796504787700469737820946806556379851425348409 +6903484946126406722104799612810275531016737884354336960835748710713062889887930835389522081146458095107803234373499389860789149 +6113798456809198989364723468291582407592480139088852307707256783165897446795918270682937158869421622443360894104220836046672216 +1297180429334742843324101908319081257574325368096788573404226330238491288173549545719251363488582787945804486932260991191153289 +6310060489009039954463983763054135671085760401926391779699811819750329003225671390731075907159344886944959170333579705985011394 +8928332308562582665591113793712076278529561597438667031252620919756801258278599576959931680328088876910755153375346545987026059 +6122799394738485298698282219069449469308864175448069521079405422190290510333027132055402559422290946776150886848955390869348557 +3298127120443406100122108547957183131539174057790900079286455780159808161628252012286085211125545510059619150351442374519647873 +7895547723969636832110890595697353438517639756208042458909186966958825057542753856885651750056062715953653156961335641588198051 +7242277604059435612451710438360700839539630420328128639238629662384680381167202360792624305492770524378775130253064094350170239 +6328910744252096963453975544360333319092492567496656064841933375445672717827688660311871137666274201081402086421470543791139037 +1263075806974163649200998435369614522606717804984408053096263479824896274273438278637073802016955266073407127338686266295417138 +3380472791463934939167548524939273036820154830243043930199471181363843017057589926197441097934643398001941245455025305275718778 +6053039097745341259474392045998222000183392630902077243215225896980913571132934688432219430448989534298905581203790323081204451 +9398618592749592673582297312578194153271726432191886003886455601016177152460729663034185278967994135744387261240974218700552874 +5660578626067455796320901716488860867842597868415619043285914590213707161427735661103212503097888685177044222466862770614738888 +7523554669658437443860955048187147707950298396691077287510802686031319997279006079308577219459278947413769480723164988814047848 +7728230896579900984081254526628677638403672817908586482917564357430378814260695955826428360637353362697491648099319657648279806 +1471506063396451021205086178083016198973053283515072407397794280863574093292850961361893442271480652011908263493969049518154719 +1271663389268841465526530211516124798508874084299647641127612963439336172485890973748223056840291177210000865573662838724859865 +1558003045612329244947071838328209194658672648215862136590055290358556159847830215232765640226279624286079243381635121169856392 +5235686772055013347630127336838697764880220575101940207293906989459723943700375632099865606791689918944793971999840022845627855 +3362608893556970872086765317357229622168187942063554148251194799624231946317410456787560024770536041305554794440402555379048363 +5400711312343982155393978124014413441498824140561950458826861214386401267011236483424009049988004220217832202185519723172983734 +4739966966771226209909624912450025675969022747113454805529677743180928548967947375791545038561055048316262023987945828187738406 +8806383430961455857801815047901978280522034447065757458634887588704123408758546678452281010485610787173222519794754395918755668 +1313032694820263690081002684124072214508820793025272277314613304407858220279657882431787747545642867929558977205831435007980692 +3821423807029286765485523471547535800355065560255771366935624333716101960889324812432465967939307158664437101875808531730514686 +0552828401027435473969844207309352000803571913860014776373394805498998489404828887974801011916107676224063398083299850490285852 +3252785594630414326772320135663653623232974740345887923006984684749440127689169558676316478796812920656203083787608388272541143 +0886829100701462879464273672433682081314775699581471366939184918943997271841997863805609882688632845996559926289731733309941773 +1588621110929737275611330049699746179538000510503020090443485760166202124848757232989323035029655555830280894064603577880623924 +9460393092105077041540924186306320293949908924617136654677663794806814239207321566509608838781326729111423409256038330560254768 +2457234134203393682333223194421969884096521962196051357781698196280191186003044231263105047089066125824374037625184294564841726 +1139020355104202113948476242624526315291276395095210708873598278111336130644448701938653730158050834519082575031500159059606323 +5508845274116902540996666349891489554638610844733883825695366107090243640688502082955261760220172087743405531646494097110373564 +6139164128879727447485151113660365410811051772254913947601863445614936478465117644061091010800284725353254306320280311126282508 +3691867968556520735423213939515627425003649240791159831598564334907098275550400308993911728901273767070664323401959466199812550 +4385285527314360012494827503644449196670340936661875727271329351754418864846693134514944138823292825239723190822861243721969511 +3358507325257641984977160116136111658256532073729689685425144586604492783647635836225848095839938544002404873700622644589011676 +0802250613087293781581129847507424374630865942136402236774163044580066760450481644654868348223850714885941401523519942001370346 +4033651642172702525694267019009943272304377112789600524789052835595443304754361088227651039992096267114223436584457492049115625 +1008702122009370555007456522799161340685826326521804374437361823776598365128302875870953921955751574077453333491368066562183540 +8297016982983877478208126579509560908805708870139177751490524014204560610778740303237644832086788493774257972604784310184677682 +6595999611158451837934955382339066842486744152636969464066220937412139592293869816659174158028425151865523439443968468476337116 +8189453034430471397425280708179774110697730501130625090567026833440338110187758034977661201862518966699911402128869207946665922 +2967301009332385012982602845125958037212145265901920840751275900004234804254302792404413397360100212415586648851954797337031843 +4621309795646253111797549932297844132545282162620476457572874699573469126157531277320612819758228011208417228396419529928921453 +8015689409373876308149147646698419734053674168732622753328286778805854267784590171527922720365829353861437404497654095122385810 +3928532819894452405285805315911246752486216644456327155370715615309647716023031992277092232949890587556921144870096925641170977 +8706990895052933118940892301863446619427769704201470123344557113277628075518257745853511560735206426541219526593186205677069195 +9323565048038756996967463636608859147371871703253988562924960678047445864387523330892542610569708364248757858563962167618047606 +7400110024724615888960177029937719273668926142229826804752461957263527604940620456908940268767529855383328656980509173310228171 +2961079316835291946492496420673777366632570317335815018560521820389694972648667220189874230520964455139599084822803458488491752 +2053325276467463411017022059798270225751157375504499399426618848961341529109405849259510473132610506746820731096435354555540869 +8022085653261752864363248353443911714595466837953116893617432029480864961559617145874891482509931363773686667586008577729590619 +9511531059462330034969446429670396910393858411286019648142180898913734931062268324002479006487407711948954747504728441451817299 +2402899664016344765585783952968436864461868976736500943447165022135283244224710415767003722013469693233516880183011359108783628 +3912070384615994999455640965424049848090544713337811655011624123059617272139004908000480810430015004300019350285545631530421459 +7040129421344930471792157420616841533490962354840490248534268666426427664847776443733801972606800005209711387678959754783758169 +4514744752921544867887197341433819468485319076219844212297689345457872671062595496333380534044033301910072934964202067332750929 +7744301249953061021059801665718252827169031698368805857673827901657376462120738712603816558346872095129968009454819539063410887 +6694959951385441493938996005094705323326365638007625755937236084671116883434472854253964886004560210329640317017985931432992507 +9830755088107356428137960573336610521897465624860840691251416368635190221352342520096074021567053917840102300469951496094532646 +0386151915867137989482068036180096232759486104299898530934407665433761886348452364475713105652919177827149093478062390353337982 +5026237611119968967543921268886408485214827943839163757065518470886816959612437396882015040264309844556286249899479340530273455 +3790585606726228419436731177762295480034329918020969162076958086843994331135466429174339242003665276457867681590462499745066166 +2010072822988771442333707800353630624045735130580706054631404185963369356383377732515576219388977808173711224814572042638811746 +7799862036041836103498954213010085023304912197271099528776247085017591855736447939941510010043072233516636490128682332435837718 +5444031310957599380550217779540801553000957068137470959947071825380695799288846148959315246034349773291226461068837384267657314 +2720831922046271298345192916201975278989464655822747470522487119845175483171802764985807888092086888555468865508558747670561030 +5037268239030155207644532217893862758398321608270496665238719184860604497280537678204406616537105359104541182047680859111844623 +4406453518829809819978030558459040108565356220631255519247977687593573153889331324518157228436273334635625807524985686484922311 +7999885151695902136577261421847256015439589971601084977870060297320803728853045230722764838994142711659330991658042253610623719 +7149325976325848648664235796296763337498794913699316628172271388813068172391895353748736320858783488531575015143960860926515250 +9988100123929774124197265328008141820710758495752123486181540545695574140507942678614702580850949656109084164982037643220132234 +0375655311441060929515636645306301671876166021260474227545267428349070029167644989380583812184485451805574601129180998157634256 +2128590561140195400524100610776393812855470570049174388092975612532917892277799041074366790055894057834557983976227857050864930 +3547444389508771568810075527864526064121655767293193457033048336615284399475478737509966968763107403398079684015244980874035979 +8519927849602061794266560801914694036469483013102023144973737181284943231700919734522079332185384045910178109811932848109276138 +3567893790780435626536700059643971391023938979188277544418757492427060624484762199641583855650960393640504524655219454811384399 +1735108417168233650163860054247929680595797171838825452321119827231787146560622246912322232224942016739093046379276365165998295 +2756695873865903361436041234030623662053438358924238813701743016813195378135003338279782291211315013357508842537428877943825850 +6550701509812921278785065189524058197762485207459987155095767921276575821627138362297358879947692406013849533700244092475658064 +1746417294003960060064872265575781963057635907396407260460748957479942537372418296161245217676807862873866767635894044939661427 +2752800199195253529712854754798642118106991591404644917342058968657791030752834046050164301961614256798982226629143783613672100 +0156432932446788502003627727688728508740309980174215347673828299641766130526832968939502574943042109833631744090112189289099293 +3949539215662926707735521667894130134427486982796357220482855624651559957655963754443694394481228836760254413314867991125537370 +0833694740794893624914563726494064663925162779653035815936273173351045298468454290966312347207985664544908635066940292067018215 +0068104260838913669470167249521001975928318786418160510709377212045134485178004263296128628173776672447600907854928188803969244 +5213186606414124173974080816812650470078589656434215381413340547874523982782714789910645183938314140377172417321651847315127943 +3831079213870153648590263375611549325132489817138611364392701108960231363751036773655982850004119077364131468986709426279928740 +0816735516514168218608953096598217254388061619583961807831532937684267549690578384517226873045647249368898189499267589557763196 +7614683199694761607567513771357320522896560891215582525317620218466204798420014498841517198000714225395529432437408592988810647 +6493791813371800888731383040408467219612486147146096499615909931691397312978653723829961950808111958324158561777510444489457609 +4673680562966535580522867179325650694242060572148395328716350087210015917993916484085808071728589768362522840828065262726854285 +9484597285206848463033004828740831651691831183702931423669671299973340807479819361399029066256705512283915649297270684923293120 +1714285317558703325551063623439102862882087552524008272992080310001454376923262343097189280399813634553559119888320098054640112 +6878492562141548781762151287184309582512789444294394903670308453724775992147414690794148922865626436020007876554089383158780819 +9151911784071444893456939300122542765400868095022473582458690303654492910301968711120089416900205538880607052645783814509878005 +1882377638793327163743947324933816937365677683669086577608915703348630259888777501270806753764247220631643679580537815123548411 +6272056421434812628524453496821198676269890972424881983781819205395093370536909259477553372703891212584194254740307122112225533 +9681870026283102041075727337579428100736459914910652574617863758549452931286799490040297351890738617979595875678774646214173033 +4185695124550535533485860637896723654154834853472854068710912474987668413470803105179616610921355620666097149488574561869489797 +3333789216162396397679204330436931257560768258425513267401621836728579265694014546537584859527037491359360764924746663506919770 +2081504393567373511045831331064517139188352171030907247345004840816407938277996907076471099879274974679268216987024065310558333 +4237450865130692858281322184224268516039222037739605355087614033374925113958908332718451900205100215886486089988991485524969556 +7929075290163499129098877300162688469599528046726233844196457006510599251899484746828484574667591829570772351512078305736672654 +9048657921163013454470683806356852138380411128268107788272256300715016144618475970710912738205043728268553865691241054834233600 +7029431095139361764456603528202380622931434502522594214209499225882330762428652317267948702623321489577844016464692915246359712 +1105356646640216883904536505154508223476242075189032231521222657818953039915135319911079058876034135094530224254031516149971890 +9040945596327638323723931073336767619159273652642403035784821558395849500879059623038153539774239738033911626241510146918039749 +5283073188137753905883236902928531799079492225443087557262624838420087547899455213109577396225125545248301841891417389453351792 +7168518951278682051937542342148124215459196543677153007216757499379420036959725544058917170458823956258437616459586004329579552 +5203183504391078070063471026373757432195663011756522268894506479810617330340549685777010011742133881536309636471645529762659089 +4854999787239813251707701121392165868052289429676407362096901868531485589830231940346048063593475645736706551014468995600111842 +4955763538748975069385515923948951672972674499411759026560381765340448515748581186486358853253830173480680063909301824002408127 +9640228631714593257928903905827590703526290377038235443447387587380382054598578561742893844220053769466029427853174638397427339 +7206862882358947361070854137919196014699723353543071743024301192848154276186984110546320724917862537765542130378492262094573104 +9132037902884622149627426018038745128067438979528182768862440252858820253995972446342211888009471423051493159698724414795710825 +4044729478097558603438562691316366082632115863305337165834502683246949426265600257894877978230675496217762990369003197560692400 +1914819988622085254720645203900712417397542695171367605811941681178792903669629925720096971822910923986744641852805301639652708 +2489879698662595408916009150319958484334649360641536805110449468835050783501013718256899380316883895243102069697127104742796718 +4988423693542042725154817443015091705766869067613353141088272355694386531680806067437655514967029170154529196712934789617792274 +9999952996574987268331116423939940247569869095269736111023291772144901858552922926558494336795934151397996022792048113880115874 +1453279672186008281192597269984891593458554070326190463606379023362133764652707672957322700161490484448540753367821464532839504 +8011800610118047251507565848810514239096971840458570325653839372359200258329325658866372171001058138732560340444368314621028592 +8193050742913890615185212677939019076499475354863399017606329114085259623153218920828787136128272822531407941969673998134066236 +6165032662878134088891598296730315347615307351518186514382605872591152954913301540364688295560996800081490449469152762525841628 +6574519924837827479315694440706135165231391744110344384147108151733208482344899812368417803491072197299164648305134049390042783 +4020778253016163495293946067531529520938183648436344477015365877950549136205835548741134429268197026979922853216037833912140076 +2560367110629307999607135522026567041549325633921424597293236948045008668100091755427842685667533245402093886980655601060106544 +8546203408136109675436916166218885966337393842202215612854795887800951672240495314500504099192631873269330473780375743830861472 +9788815056490265802740571039729926738317636325182129335295512619052159101153870121975439793577984106855088335077085584239297075 +0607830196004382404188902942190311303589870612777737359147435465863424442529799140567063628056860095478745600686399779891523221 +6451811221617351847394635729026502559474989696391694472537910902735717543356566711060840045900392940941333171363021277998101010 +5131780001821762931334985227662541898093578808808129086254438607279239181340438600040947973426848418990057822839591824881359023 +1781431927220093804299065870243095263893435123368341056371775420733487563878765425465471958566683414339834418118022714155686281 +7855784526694976383147640552245153733193000678558101047345867400675345449097722230036412101539440443445801673442840764573276547 +7277064664195765848534013590019916383557749169375559963951776576181466602296914352536749809247940247872302329782226925806468043 +7192135649991318277247808266477765244997880386860464765070023950465490749600254005312168230797058556941811885358619067264540155 +7745942254017380214048240376624237449522972214173349323588251697136718540827110673334670762835744617609275818957162829921297194 +9308025524075793350930587214905796399488049433256186463627589329393236892726854692113712455277309297788479077266643487682416715 +0884436213404481146144542589017779230742715119840784000653264872459751590076369675379177572780987987657953814990135600852985178 +3730124530298931373240431341115710398974380360265132276809345755075936226833617497570877144367390338090832665519341170114016004 +4487614165841966483390809601332500844989818602599615930254525814591372642009685306021109318297597415196166334778397096512192919 +9738135052767202788355804851260065753666213740432424577877433994081030576175406007197406894692674226624886870624390536370450115 +4972079333970854412711805268652812255100648740561624233549801666164086630826247751458218621070591048081789263645429911196722042 +6264477153602121971771346665126521727114186506364463613567938860398188315590497953212091430368302124372365408850328299694402187 +7096923163132078408222622634338258654575603652287595704602066710353019363339205986259588116309830845020074124235604674290929803 +8620319232582906151287500817533307221837336344013921731345438451489042597967502976685688377786979657248449082964352894633940456 +2338290774645460787964275902190755185934483199622353782037807590665967507775691698706742149485822727584225845398159573488959138 +9690411512559467787285374715845943612220011261976368220307963458032917206195128934924351124238497339947198608551639030163894645 +2522942273087160899325601687017261599086953489151039944965907240532011926664847298253352884442034273091440472287878013270485552 +1149024375555901235741559582840384810679491209306220816791833779666244374445454015124428345163452944670223052356961455086641846 +4911582198911887147216938492382715818668300238279079306283822841054803949022104243393368739761683072273825914099989412306146198 +9386705286204083264591504191247314048641049210231970001783669995174466309042384199881382505454205575494098905658585266777083412 +6597181522016787958122306510209743227930379650431716531860926286813430719462209935654406493573792531684209677728726902971780496 +7454713877871827759953186012939103462107245349313356776395316754209733644734837949073664445059496108161795213098031298387675255 +8608951097824554197185243937211248657800767047324365682759725200979621123783045604563185385990004228189141941283634284151650878 +5110742662179788336448957887870922104701629467080657819437644833165127617594897707891497065965495615130086622172410875337801320 +2393400609667414757827986008711153834467770780863121443973568354701559598965757992398836770293536284505657099974035105460765424 +5696578078800033701889220099620389665546740070731873354278406253021517777757298261519059116566359233204858968015644380662525991 +0719487243613892918578289524307174160805716406329931625803050285847534463223682481335815436403441752588538391995273084423051287 +6507583132575286651290896026205391515246827860101127710478694329951379556419208073957900692792049817183622267407642643093594278 +1377392380718866307096826670910831948575020464783957275980748872622490677834200043136176315679139636646826582155786548373039619 +6270395893591295820132189201839068186418319333704429754904087462921395570639063307809781688462251183623162844581418406608429879 +5043135604579355727135840436265306254474995931628655768285052525690960177800386175175072542969753822246288489006113161219380471 +2468870042718971405890990067722938578347935929978019468025646064389136067817188723296206441343678326386730657242344326326352583 +4850753963828793552300794217123706294629474328454245842829452531896699931041364932772984481884556840376569123447688057207356583 +7057042541510451935323810217032483925746383540989928451101483213770975125680075200108338616874937924879361446516778250185550748 +0832810498555353548220097752070786325088219810053096162098021215047558250904896030368053916311890910240442204298949146112466005 +6040761853883330139655359121858985041009457655416978338162675746762113717003221699855200571002086517873586911211646864010901906 +7256461701161490507120388412005575681572262509069280090655692691681966297281536854053796037221572903258227405845942489932658062 +8784119159896217889610742272823513831023300901198251714365380671379756902059751391678189474782942687418296669663414454093527211 +1713004342725426977027637006978967397759795215758374886596544583647960566243025979142738914314172995528789806147213520151545065 +6148932160477331319836946915602714548850688076504811022908366977684207197869071392739615383347277286078886115107459496844571114 +2443445647690017845788068401719782165485185661849045242867722200580611203950026796072201135775981201642969396670605705878495845 +1294371619212953132417647548708472622197636506856370676160451382994700324005284453279834375908065277166478082791508699598722770 +0053575986014268796134989286247216849523870686352637864684792640382519878279886269694672196689841813884729761559800125789968281 +4079017492141445585825743793710894771763718871198578437956220555470157513268225528361787107561094227364687598864249537846090985 +3335615421853085612471428388452383268898317706526014807014690533942186796242785098624189594771605450015209158202436565395838528 +6313851017796457232684831975868577197008285743175695149676285515896251363246115689779921987378115615609824156064808941150859547 +2765806218554516717364886537785391971358807028025829434420213828068755277959823238847926003687327479957197440099922200245318093 +0743837871847609090197592779267491068691643664874076633968061486254606691482824855693172784961663018432092989740850414577990750 +6025684120312451259686658587433694029989987324349789159690905104220972232002229613273265979326232104213906084669828970966380427 +6946689887211552778830098495751925653354785324514999092963991842666284889259310987400348941690948665027526678850288865496058905 +6762732711489921976841952967909655635081188142889946182277259409310226741889437458160117435942945024450654816897944448992096154 +5422044978529069360644478126257492572807446085424868838210351520631277115650603746786970481265582987466949165123354295111201812 +4211276624640084315612918421177060204230299421803486633824592006573340606615889580523464227765304350439006450951838160825473751 +1889210243603037535646067281045805960008570285172891727145769267831573879630019508324069996866828486890146852983707941528252170 +6433944896458400005722683036542919674416232401927258992043251760109983309622060221449241884161395267285324969554809613236559401 +5926828496890530602862162152396620975722541268836002211191264684743325984168877494942640619948750726132999624105124012640906283 +8437529190279374121273890161340277025282882572337700420322280964590810755716568643364840307879983400203857287989101011632690781 +5802276041061620122710926329386353739806320420710281218622737558209391349689462899753335081329268758815057487656715890733084223 +8915069186666442083922633115688459542249351209368361689010535548757066038294153139851377020698884275592572198938097053313533421 +3618393873597153740469607428584632822338908997205070091595456519416863149036488990139674096344263132753025929808588767591996177 +8414765535715090848046152050822658084473018911692025473276058620384118863100457752482347145023516317250246454398495927754913591 +4409298010504839698734131748484717641239465901635152376312170896985346043211365815426588368753012122770591860188057761705660735 +5937603186684235090583457539320837412342393238618817530913846014346149555309319385019839106489932216877734882102311404478731716 +7896182092904946306296521788524236999972318921384942071102716941043024956484573567729385887708261561271188936395359650621440543 +9381772394748371157597820184835066654518409316544350915121960781236322808659004922335381181699829103134517372303100027463245782 +2615260350961768130102419692323579521155005867350775325462551282112894826272075572040191002407099324856614539494210810480930402 +1838597774381264536500359028476517592435681239463143604712064430411599943084702827260512914320943321785685269655653280527187527 +6303497221483801786682166005068915785511014637493079986822276972242808097676862236960849777047832121671397043713182928176830651 +4198254431094795957177601291882860974299955452519675630067202852553365287181631847719984611318356336562220811004590160639271651 +2089050062014249099555113392986787327862396528284354504971691665000330719435672605372528268724943691397193582081997337212460855 +8717657741330116057938366732697730717547522872552360477855457981234742653137379848417305649320084599648948083741462795882532206 +9986804236567371664160082803763334644507241277854017370156980387812678877717978239570818728942909512587707736901227099845780710 +1187012467446764171881903676699687486363209718049359334271085642494010609014855350532206473984262423560106107436149534409159662 +2848539362765385732985316450544140366071147981646641820740677994697597799634870260463776941968992688323272637389950097251845343 +2181340059702615616436136969810778034448257219485660110664564303142589278244498041564775661983269433643752893242421364842071587 +6511475958145031560867412663572535560224504744829799500512261131519166232318015397419472829178929843115481631831351152969869230 +5907563295429072630722334129008428981723806690819380400545768764609783317272102244876794754684900328466037092277858030276039049 +6371121545042724094475375695524854871280733127714683231595927748253222937280791764025222016369882957071507439090065827190758754 +1326490274618829941454831367643360383905788459550667904893538378442057279259437234426288871318674319032806392619399812416955409 +2775275011488829091588325070565060813564196841900611728756018532857865700574781000487644896468359788252916194701281784442869238 +5475404321259151425741175250657761031096608313986258427026901467928877964154516924617882931075782136380173762818340243425882456 +8098506275539721604499982773326806644420333816118778369209316210417120818431663778002427348509174034183157653013145092651819004 +3184928471078890543345386093573923680647427432807794321314997911033358058808825224490515008557540951525337845495688169454435522 +2663993815050031607786993170355950506772886776860845172901406916363156294602197804936561148186445731375733299997098917913770308 +9652206542382536216286608096098963262843138133196068261154952924480494400928138047212132673039123404498669742532274932056703877 +0803886057269071094490544437295692503919136095718722047240291609927251689451657849380414877514143018375735518559305335464120670 +0151936480170888759767939879997438315844081206201757776198680712499035405650084830184555439755477818487911581690050566831394218 +6229367230673286153903588882341483871782570684557378319370079577894075314574849976163949953230620503498800993353619885098682722 +0775734990307483598683533921005381003594918538823910337693583925400749988570440308274068133400598419503446320537735625031687287 +2279164484478563946971740937436182665660391281989609876479617857522997660917074445342075564734477309946731477007826609533166522 +3758123683039815493345731508244661356741376276550143129350958441510802897891765417564525850067802663207435432004216770694226804 +5619370786333060777254420771287566622455383721762137194440628363338996839313233289419808657699323597544032060605130001017679348 +1117350732097568268814555936831091979923626150476679569711035279777989299558634893220687598099570940599334574372587146302188862 +7465078440908857228369280211059007709404158916443923372217270156208307015594931088483508995735817533317081734115339967063512432 +0277551433478001259984748926007853394677528181409165584238991766092184666080139589261333280556946872045412337141801061054086949 +0700754560549557192840941960533883683916477333049889110496053434388836650044089728945956844381845980096615663986249384912038818 +1729833498846421646108320742580046475400334773592611265395391377127688594599285540845684396076777993728243626683212706942573378 +4323511472129830807288869567946309250674678068275114664508496611904796208234885435751853338278854135085398181237686024172180393 +1312943548505486065871582599174831836548648084308970713256187466678464307897110260321658188877464727357499093515658589884692245 +7139584610276191442567513021000360376560327542398104492743671139951823728316211591532169872633220764756742780779371664542606277 +8013300125828426771757381146217871760111879193645766888697436656318066675243280906292568248974540959337990973627561398675008197 +5419373124232954726590126551016645137142696380830673731392642951542537238565961064580394978042654467730915963421765890733361548 +8922925533382884854249653772799918390864697404746308525228860651400300505857310156341812355891928439789989969029099008533806665 +0128219411137135309941467559688405403979836275303036131774092714874831957845954239946311223981403764141665925425006138193699503 +4023035360745761142363755560440975222622652382698883967356728120684964102122995320575139970015373461400690787473496703365503109 +2193654449177213661881030975620088565679413977376323484863510871705861613106991010472484286534395967108522932664799263272973605 +1651932770511765948726250021128129739307128956256839990590743197977037091662728274789578774568077274689428101918180304889077378 +5017924617218629587957879829522315523692604428645809008208520226544692314383658761425382226132125228054011492055441635314272210 +6749666157481037578188762106676697225815856020636806090419991079970372071289788209874954711658011019178097283297120172931963024 +3867624578607959056494862536265389313506274108084087969185322137028561490827818437756426384470803080867883480797295503488443754 +0266755732085811371635896735784706558323514361613693558482118358007540674486152236347627273339110326422500120960164943514979392 +5851591583094022546650594344855608619956936942520965617453776819147723734709318492888126759979035908706037791854416714517089588 +7488779820804386679237327683610073367094612676999550051056279635818725821831859618176236859057491859294403215135785905001415728 +3397867405536078184923760377846341551636533657104459673180071634565287960125731400588939475716477977856211995657191117602660471 +6368419292461966253403926350187375212915416793685707098775001247308325202429711397391995493844090131420300451991006099818565158 +3171884061798508018743336874067220372374917564318502076615548912819919624484325005878069566693150527749974672352053137075239471 +6765298415501192506923578604075674679460732604669110708523132941578157177568853063216936080050749029655642031375231708316371762 +3941656184595817536876952668926168956461631341364895778129612449325569155094729678326528659230305783051788455638170048468709384 +9344704062107618220092102502671070488066523904860951808964071896990152691743696929206393427492236495869384495623953241985933433 +2329954473357680885309244441022618801570174231106127708621760839070677029891079166854592191885991850284207932518637882898896696 +2253197905988134418498970803242763736997197997987724202146481273276883471048373064146605033007993208986500108914474101742235859 +4471673346443282932636516218151183887721959139730007987788845685281052854907139082610271978365656275535187015151917676999391516 +1490454357895502187362105850833658593130025904397280081248261243837433753996643256759896119495710470799362993945917928340275859 +4187480193910225053682324326478852139878506761849860931936128187212907303727615513834095500089467988300420504891523377586019041 +2552835341088538861109696869815032626256269959324139802181242249722241877212898021098564840649196902417063584333206994390607317 +1538928060054617317449920025668989345408124114562616395808759769278572186743963855933221653177548947045222630757626854233433755 +5185848360760696218717214816135397052477034052026614446970415328439572221368707488898468973353428786860987024947961924953899839 +2985590716831731870038599668250152561736463733069492744631154471689685148003517537536069537952391933185169671069870236035572482 +0858819385615354860719000425698572982937868475649379251912477519089804489107469755432874178918458772943168929318750385686530082 +4333886362870957848081259613648289163828064171655224322239708870842553708643760011978783227657321411272976905508646641961780912 +2833169683733846355069467452629978104797868384861004576520122658047677739451418977045812632716306848990938028931383957425588061 +6045777379942977654526927417023844498064089452756699867598470436037381168815462649457696613017471951809000988548875034178737874 +9160331824383077463591927113486291567227954453778702317129507491730495740733772073822607464672707613182092741352901404225499494 +2171629178354382148962537134449119312001738349076684871759101972177169632459401670960405086152157843086489006781537667287047896 +5465718260392150704046827088996611582230320415786650066647900151553492798388495126103785840394545507668150495076747239741124643 +1419544523318047856148530079232229449051190304992694484547233964026124081035121194889038106213961089629592355710172833338936041 +5735074574334557161220421706612819925561806730824425800383474349667304718298810588448596848386045836391940535442755152772209476 +4477743831927353467775816826229193246462805293097367869721250682503407993182691455177890744118461259199550137591872314970843570 +9614009267031215091375702771630372739884751484563943485109876681877458872527820916316860531787616767154557713108421464243252888 +5725244451595751926785755645396186934606833650722629688279447836080615920686865486122499155337740847620671316549690747377719416 +9886724243384291436035548475563689452903302006875551792856774087989888845839795026561561511158603968657727537570783565987096483 +4338269257625836943750609173139824275314999458619895874921046346622761766613817248643045610302260828228899586867769128173831852 +1927251435241586742325643335703829021262110618516121300640702167056777723519853174450793473043913595814696522118527504244796457 +3599578411448167894106533981801012494310659214850381649569414007781396645447499689729938374104814729448345077680781325687345087 +6129529633831819171439689515282408943585760656344118595138870514119273973014358876594274788901442968142994742135926265337385656 +2522744449484255252321589542733596606602045925005526885282459116039248444399754430448309551058351839870771919853241700908753171 +8095397674156020153025647112643503525783203561945051043117484225509753196255050991995918313572389826508756030729780202503607157 +0514732746209410237098921106270758705122238404358107693018072790601247661147289735400790334022315641411924147791473123654604067 +2183481353624169330708810250088442996270202852800382852132922090252387617856118236197486397057001588957700864557493511482566082 +0159554686938426672550427425422201760923684567404107605954807954862252513544057244716697622930133085009155635119267747535809775 +1603870374074406842079458547774759610733574985005768921583657069449807861035511349884403260777475545081121567326386609338253275 +2866478551802452645398860404273568244013340206033503958336009745540839717584645607628126532817660071722429335530485083943886111 +4372806755879104949087031501622642625790498233747556727536449464577828956704936323795941826197457122033168971930280473743209480 +3510521745467987614604622504324811929598409501462197588604391588467606320835512728358214802216357643065451820237801293132895101 +8869176817884529359989957422435201680723727230870849702923220814297956988224073755455624237386035731957272496882413670632312027 +4987041709615070926110617696999725204352784063869478762637421619822949085893727644701242995534552198616152209092394050930669848 +7400973738218421741284556096408600962957422728138542260382229658265717929340410249216494966480842797114656043729408022372140555 +5357622280419421430680077268491817179160449030679507850055675685514803114136633487732178706581280425610186327901499849008080553 +2063614935667238312237638365326101007587181929899519898020565403780654677915752203035463564958973934599571280127628650025231204 +7510339610097271890758575355760181313472729121504677949019841665976993380277166759244310422659174282752504901557558455360101043 +6704624590746564335785852066839780043541376118703961641315331313655337920280405840841448353766789935381787637277026193365826955 +5718584531080420228299782199614112550723022759714938401551695221278973774269123526563402857239118115571007974128747966574892058 +7406616537246908705363274038771943716880888382315653131428051669734785136535017075892856676521312977758275400592055721561155447 +8191030503034523458658590430731690106155340044400945735308786298326190302983231623869556707507267012762969572969229893222326020 +2781627756283482808592633115010135954787388098836449433663074354764942592857302479394320001436105974173300167981736974256889069 +0749889909665269146510382674032370824445149525831032970035921874308716508078712017288693000420564774687445800320014347768102977 +9500582955080504298493442003839451470653349230762999448413032412395803444854529134420395637374428892260393652466859653474793248 +5578683404816116099839255848388989533623282136956100112356341602163894219640627502458957004434668830525937659394378712613593339 +1328818081682559186294868249152029912791110338139114004470380993085670292745974492137182403037306610656137695392960137614180452 +9752978688889728250401766859342517990190156836045937377483717028864519242478723992053825465496992411244434140736698055778811865 +5764034558119091657996034001136138512460220698471156208113839665880299241195462026376651406117658603193270831455055313097779290 +8887190533917978952783942478895262358255953083684956216049958401478271139454139129915935858174062762845986202401574727546757519 +9965112384492455902741039666831644918325589401840520355227650968749201419553213336728867559952057316374095495633674649247673155 +6547477349678905420240149432846633096396537735337479158019850832661246628844505249719392841713290257003652108726047676880419068 +4709595063724483081528398865202332209399887790016225322294096330311019868576291990108551198704968033034014495019187100300731545 +3677983555491782274707240632393630038558613476413675438434221141668472183743225033730344641440692723378405837243484642959062746 +5604991163272359834040422515862505640037839519050818603112192411600677144216422234215431187611787844494127075718764452791848799 +4854476179924301594123281798645361490338402536310431288082224776520403651635476604940174850365038818289755315902382966023203577 +0810744335357702561567797304986145218660883831691187353402861163846458499952189536879044115452318681728624510324860015033788398 +3979771283995380891938289539381044182171976827997056764348788204187193621090041996206584024348644400402675325221299998818648561 +8560553235856018013965529605400965645682933039991913219712272084018424710938107420465401522172404020985846364794336906931979136 +4463662321945447989909156167276620401617659159053053338198477063314745749076987397606793494767276193523264007206178167936213596 +6945045570777520341473278974105523918485559285787445842507870137014286454851350178004328369233461521852484045058748849569462881 +7175394733133381132769602280613000317015438488548919976504824538838731947546613872200764083627777984989422833194236427886436510 +6841401583932488101726607361270514543381198032082148492961394207816779730163687527133913837138426969504533356370971942113680465 +7438297763442758000905394998453798098283316097089007654200747461809281986645383560583766595972712154827834692097585224878349332 +9365726487547590990433290508632050107564671461069290060500347861396555664994103630437491491318643117772003180597703910105848664 +6611643426610581687045714856705413399193072506247927181637882082436528718891021527134176567154185837899481317325358321239700678 +1557904502034099580005949862058754523860889792700842593217321816597173581797908357156234792521428404814333336278006477527454771 +5551300020237036342120897324580866490216118116991149735001002790662102209629270626643498774459628186650438561401066616780089887 +6490963755355736580386978526346909059361347064280111211229508805478817906115930155049278075845715671257641653870466615953910939 +5214778972551839841667874021257601195235129654518890488743898996335333591775608284908553206841411120044347642321931257964395096 +5963500480745033263837415489981452116756935833729630104233601643805643301868453891208377454731826714298746739883158478178624208 +8078825488224820778954240186836158374373671746556003072588357835036884385547573916766540198508057742745190671930027702184464479 +7807519854130748159210155879811678899722270728200599689770154189194726372412310007746266228667023082290285051427818296004435318 +8849779730758648974379073101178990211676693852201291929217145528640734853429497077571161674011066495817276757324157483849716817 +3827352998411927949530005101476246283441759897176637008058185334546170312583858564041585383266413483217734023667361663656510262 +4989459887640993845875939547459055523785574975836774839383442442772727790780196274204446558595371067248953971633261788681833422 +1430260652446882399259326117845029992780579944482914613560754920430392419419801174119306529200876322246749783122971752437529985 +1073402550916699040362018956704245437401832452382196705545206547444352132288265525961915924743830388330363555452775455714221746 +1446616034452999060536906307198084433181209978784238878045014349105221877679941993115397416711191417693894301857690745032510836 +2955390019036983033546377278387952218996955027373523655965784118029086980123579985775681000785897246210289721704232358897262443 +1116899333926038276430989136011993311064835969966384464160468735201645112827713375177166600414004561963808951457633980244174631 +3365167597668328545027411315066184915713562624842225290359331084124729347322094220584503855828183282959730835678676639554531692 +3703132630286521554741077336255711977522731641435705473997030102644612053700886241315456330580773390018414386585814426113751822 +9217494111509421272989272558690493322060649433443466464859990829891612802636240902558280843106767641519024486333577210250455758 +1991150751561946927232491146002439007601543254992434751580119323409611022604175456900099304707614992483865118197257172247468485 +4718289269042762658615396540607439436267108037664826892947523927247548349191063673761417193077849996352799507438421286155035753 +3670003174685490297784963434080516316824119943018705327982929605778412196093833118187456471893403114829335230172542023511761918 +3530299462397538994353624879221057016014513591355266971800473995195599575301873296387357918288523036075230002667564181209941862 +5652465756063919538830640228613516853681348031923008502389424015416629283480087157633179737963541694567718976270634283296698772 +0628398273786061322495772855081114777882254155541640367813109541283460927308493462443462004237680494256941289350455650052190389 +6530923425528750248516830645859529161548508001636146477492907827739674571658452569696309864671893300056851989854105052764500328 +6377838408857911124605696723808145124469491655188029699697020610066790779133275460162800601521823852930699312176096876169139587 +0939964825101402137367079294180090650010316165686145129739428563266232844352675969324123159076365905057635263730951535508592474 +1811804823447992196520518580949056544426238843325335935996032408627339563545875884333166832898448807558706376176980463476019811 +1853671195949445539340843650229575714750539986544526174232405759132358558190475375028485355385394868549681406717987759935904458 +0905656451763972780540932190579104438556329939472118207647554428546869843074735432587357981463196272333295652269129883611649971 +8081235587163561191593283995607470950948702816215035438010567011848029435878836700192088876498542980088266248768136664360732349 +9048395346539903256416069243232533610069691095414640231288922049229368354092313481960519237230458713935632653201847905841902926 +3586899375981532151071970206889742452371614510846757239619094223086588901204291404135214382914427118575565088049023826713070734 +2259183814776114935570816268571424671175423119794189492522671432476564901966405647187984087627618260356094922535155595764746633 +8775939779211866982932257974707044029673787248018990390269181484731175023868082868637570701885688930003111950730117366747034776 +7292037040878433775921745075599110933471784681597260046655966261287697859339714368407310137037826533774841074326027111292219013 +0255740507228750627788675142293353045696200562639503837065555027761345191438643278181859657112500994447007605647817675710357329 +2976075331061106900800437561657304517961915727589721564040507512613786451696450429048216474153539650176719572123959448008900012 +3408433706045747990792868143612079234278596536479367304972783738238238625919742682436942402201332632706821698633101418726786131 +8375660756604324838036937822438189146692980841058943655646096987446498055875487503182581651007976496878268593478897138465125407 +0302161625788138941756180586844882835162635784547013096553214201480525116709804815394793585124634598219195269074280104207728072 +4667838900407600665351205946634451166183916253892825653381319301710998507256107777186848085957403008238368100522944303632213848 +0568234978451633682200113830676859774853569376506354250671989943595147694543341125278943952146653132296950198184251693558754266 +0837997184654618266464229835853893240382511414642926848175904359357425914225875122515607126936089890130506925127703951001817058 +7698607971927804322206607636649779969047920064098738740542516881736563449825854806194526365935192809082449412467250570813092077 +7597321887811111154968364298503454292989382990719881362442172931187037500402048057328677657705369081382090672410230632333022578 +5702691344767100466202486216169170905325756285673048601355109036801723677816257513289352210067825399512675664902709627155754391 +9534899164623786842509061739505443727104012615679366121874758009252764781464695793453679090140639672566086270483355150124722338 +3629599380125790239429932208245186801604452556987851483665390481177058173633973798236252529147955843797090679456499291551397899 +1654839141753372209578242837530384383897899016697607163271741042796206564645006293555562819243718356635356102675076651792712069 +0330578183767799422174048823749345411130436371482070584047136107077445803065143356378310883097325525812461948426463818958359831 +0535837083229949247061916351323305805073161284351843348700635104127671026579915150619625693973299904452300157134992421628495782 +9135533592025422741289488252334208635046166354163256661447685205945401888767478319569656787569357811074256962118550453119004362 +9301751134736346294356523345255306657038167822211402293117443464784840025659169376501953957672727051530179579688902595680131766 +2105969846665539329882900056874716520798786759023512793122449365556804018453805656203040461898429839786920406056455733868681013 +8994359707951012841246179634189829024194191300675090705148350390247850109295388787831386650321294752877084504137894482370739618 +9241947230769626839032573755018849701961689378840022528468670515360403514925600595045782712624516468191893178057825723173315417 +0398704518600034083166938112120280977963993296425790225059839016038745051472686938943167918298644018861694001602670324180907739 +2829253174521666979009820015551029220993105373115142695444414232286946938928961493390386858301002697320226848958500348592987936 +4933376341875898717948810316592654389756790528913140902980810511129982071300880573916140408222881362220907631175682400695211957 +2728405831786952505365442116971677026094675638127255014104635321119617454191227198408175192550442007477181962648595382032863011 +3335513609023390087365324523236604049358730860336918649941016742564809500323096143070248679219333226141951672644858949079743290 +2564143013685722767288600853878709769356311565059463598251742499173323941411533573854915762183775988798690410191696639340413240 +0493448733483595346076056156947860868081377709049727106924154044343435604871736849228001533150906569243527614792377672885378205 +5757247991130415486385859064419869200094851432494013487858837812428585929428086640566084149796652609672519627454844564932256297 +4669558478660881121021903646645372020041434507907448258764267056511994202274610206718051374682457426295364768694292186976277202 +1564360996091464350826834839280932166253903015206256531264131271209638480781609315412345487344374301996139188255108960560840234 +9176534815648639973257109408451729832095062649822000160147163265683826755303899059727567592934572490764864402347275743547218198 +1866055495605517210298107031806201932208987305284317169767415130763125409722583393741250961695800822861253047933789380082578910 +7563809612558319699283127796207634523042191451596228205984495682290513031662522019531400901314878626864267750108819514332947951 +6013217317646435086018804719571761416592333129643806900438788455325630320446510012694647580590342912656641492584092556696246675 +0844610636935384963725961540456988648961187114742364338486232200258126457044861826644153851981432318719173242496404738484829592 +3080829363782082227074488901159023270312983944302028828324523976099006110195688729810726298266450333348633619065288392022236229 +8005690472921247866581261895577036104640057531331680489329654614207889686295106419176764455962025382611644995861348654828098933 +8502321967575148172006469677676125932579572402208147927471694132908710196626265784804209764246638587086749717612279459712731943 +8253507041283227562754413288994720899007749153170225893148651683553291625294974124954074490050789574119491844797138467527966227 +0130509196073119104402816135401821552059355465255575401457469499625193746950815086568670490180348072693785487568778765124602003 +0665248124119376664057125582265850335568981888916788294854156077917276025925880496268355143677289608433935345978812154208855268 +8995137970182597573167629723687168196388018417038021858197249313810312106750976993518701591284371821222304917627037046364482239 +3380163047424234535936046961259910243886634603953810906816744443879125914109276466757734603864050055610106148478096732039481501 +0050863632424997195774488430947153709833716798293363139764056905080948883372556055175360309201107604690863026027029304809129018 +3159045522676282622055741762907017038764983291766901157880220174438440331565498671114637842462602216932285417970008975756029800 +3552237063449291059014349162828946704972597219745549772402644156576458713144836954411568696563098211658803621586740322438123527 +8553792749167921622407818214329886287689794104816641507269131593430918574387850051670847355476478466155909359159406347347073422 +4529077230987714405406119382703861571668341576429308147647968381984164008665288474312273798880569173169482210427510225857144151 +6873466267196323702969428729542596817149488353193513581368049814798979078196465830515246880993440065883452528995023407220294628 +3423764900702625807926494346022071251875105005653050523263476206366063134157656968651581937910705560688575444683225925304492434 +2952113637739291573792177591055762311484591345740602655920022962868733639951191003783483933798477891209504511076833970415208477 +5918285063915228730571970492875778946493408022023485280196322884176264848374770469091671934052402742006902024842930375953314997 +0188283754328437589051211543538670804314085522712102214064193375644277314824255259794129098802312806481056628400349306174297818 +7400374465412082001463306963438819366261869661341067811137716763449961651069723995940239925017934226230532596999220744932707685 +6210672490680638537370704393760193333970297977253365672997212980801329671424210333810500664050546692669173806880821364438329547 +0441850655848799432631375117017035628999017015487913470494267041654835295714453029034648907258088418618148803149605119491595378 +0667709470097778550996561398901373747557627918533058120957567808028277879620594852014529739234370013800886373620787541449928329 +3125515897530266841998191196333295200100578758020333846745697177799969213733901430202219148194889600422499239512275441137216920 +5650415027342249979224454839045392096843643602822206652387425455763721764312986203030589819856297909492941454232871627676382600 +9334860923952422719693805188600329744718934283065130982713900165662458916981874159673444880197331666542991521443060535788337790 +2260464469972488038180467803562022523783671731570132720326212442908194344858555602668263231905684505806559421525279834670049624 +7792295078720351079690690721334629651981883246931635344717773193932081371713539221551527543695110154112122644215377568071412160 +9195473483543180179899660415512485164196998646076213127620507751563271324222304453084630241541418631528227182134165081602418516 +6107199131518105376224811112336028754695240510430182476876463353393744511821873843541677824666782764783493452855094114804483949 +4681701662176678506643507046144062278379351438997367570137464357422720669913574736380880061893275748247043675527436431955366100 +7149553832033242764660394022295041894654956152213899886346680985491273200086975419330405272293533518246991447255727068177450390 +3655198436587622677364860349928319378663047397528676907239140945275682820405657108502041392682979560846597921188888907265931036 +6476533442276806500783053564071177712515831376234094631885310570178318133342242810813665266902989059805523937906419013727894188 +5431372487274298724777517697230193875880937677668764659986523658830811826735084723524757461022654674192380627828684371745612348 +3820846618376083469895407196241963862417850743410179248545973893510278183277393709562656195366243761330452232662018800107769440 +2069720833072682115347026542393190511365870201586051736004598121210837565385863777323843786272509797134422300430192141780468920 +0442031082909423594641218252063553777303614020588281829250848330051744891712248697144729676289337492695365177253047051776547762 +7268639820077382077506959745238922291525171731315329144119549984484846531136030620064784882237444115815092112452964098100945835 +9872606636855509027099907122315750101939814965155251567403611737103452272009598076995597635176083234788901670535978209531433759 +1085327638195333244518594079295886331726523118261222009427508754702667381643585015276186491128806622593525832828640622269002176 +8000558812466886549021793247340135947691306446427709528028498200426365682563584527832696795214569479985794618339152398807896264 +6187988847312530289136504030984674599402925257102785831329312431749794271223626066515601194350356075536808657593446997124519277 +8221593778261506773470868068030158332034878924556789236087379527748372902142937754401272228850387450354124993708739623158789162 +0522225048132576688432285718958345124544338046135042578516691967468794904627442698787375554323557096004260191198734997094093259 +2462493092358156921232365706546429643442698260566011347797207644508552329524610339812245114706710736520675636593241151933904714 +8168806409481440220552654041925492552985114821706878419857282323004033916474011864732600044228285384350058041710798941857144196 +8456183927145192331984325229219292792322346278563254801756089348717336352552507588302857665733398806159165657708323657950532344 +7959027432654889449179694030583164431534165468437531025326379868797173494533488353046359898220027884040904102921916878818230634 +1572671474039612214962772842653777925720919829404267236601529791378862012346769450214240286963754895280462160430824808649254296 +2713235643749557564532205594282964657763877791499599519104896455051738725475670902079776954156280521778944415643098383791627273 +6105964254598123007279257593697900824845572590247120355844905046052760198392683039070579501029336629655707492386250995234644336 +2615762358163929247483738668566291188631481608731204506773561888479176007728316484171079619334578564058039995926257045933155309 +9915284962226636771930258072321745421222389817234966851925987744510291810977458517680205953227386190750476782698954854970577352 +5673578687374046860672064496277781044455548859373704110716891640260365042726694184132711815871762240294031220477588483951772686 +8668728817231201425334885333412781573848400038328099971193361147160493544335456580227648349461318031327984792185171580102807503 +0169567182553246331404084202997183149669206523460201655479386533242862563036582654644409484005992680312894606697548079677284436 +2408634459821807360135371085559754639951676453609356475891807606645289547821918227592956096670383760258316085039325249167270345 +0639753426126492725051857242781368140995333223012553263382165242568922620303770347983405643905206504900791961601071942284936427 +0871588895354801816598052309754202243843057099627863238724184518284044817748295192358320828895054008513930794283973928514112196 +9798654324310664456635488952439998180995428408476235283937134417681364575355417817510233366035067348678178856527757300827180944 +0038123007290989063863175985592778519488056333171045377703085872568160778246582018586168915869948568385646644298618780983546355 +7869384600764512734048062223566504717806018441987433044570169614606144975153300488119826197625737171672582011169222743563965980 +6977293818241046999651425289984525444775996710580402650890897413638457082477468109575400763448366802872239506128418904706794072 +5799904603862986594106662322483455329279349540532965537255861860553280872874108710364305979053889159756036410545175996560632213 +1905113425852351684154357685504469533745516493501062212340862826870385326709406031686063551224557399310052952422838419329227931 +7546871961833002037605725905779115533289685288706841277287949763530450546184456445185142124516259820881277398739985868903009290 +5390552777664596373149764240028438950449670876087502765011571015373476441820757391918243018050536384628502031771242890575429423 +5602898175403670523511959645093602460733826479169195731008780332981500631244059144423791577718260284080812356359230721779826632 +0282070388225563293057775488479437333861437827295440192975141284220068554470734762243654683900921775316128776798676828649464561 +7044919729960370651911034098935618095727733916533962937362197410606374931103363062973825850629444853848346339446960413652519614 +7923852433886365419446901963455158024361204561948757781294971510735411634498000899993250692952771388573089449720039510778006556 +6448339128502198442312788117252393804187619178631365039260121533649897690132658417974431016238776431953884429473283621621760928 +4796673454480093007496675559950785558242785307210144883418877108437084521237518409035829359990440607609053004656855467826840969 +2539967222010309449705096804270772495367746726394308555148839954759386392982542739753442093370829925036983594533194718249508227 +5349079635800249074525176724894658939051245265345143033311987922668779171808073614407324686649849960572890952798590407394876104 +8881074380416317599148271017700792806364855765066986663556628051754736630602571628284291106232498605571402693983906331712030753 +9874937506608818776772074340810498838067037288381814044995123846119534641571274807743575095446827709625774292925956993651483030 +1351712538266765203986454072646816009684736475838189392745470191271779327758375268068178391759551780428792430541942252314370156 +8407859484892460620078868667802179357348492180685724246644370230014476781983275135684947107880402240406891839259873980451085903 +4906163533191850256268583682454988749837419823578079473319346296433799092047235338380155019552271132115175763259341940167374009 +0058465924482070913500147064727037054124622100326796664614046484792704959337155884709332716325316023891570424872580884952438266 +2436800291590505288180953642913700972896437288014085488105811040747638787748114199225450616098949678241069300416404785919318926 +1934599170792002326313718708164205938810092585230289681773510059324899654472028348104884160918328871998687720554532160558923223 +2833310668093246151983045850009608661418327316614685260920047242208439507520352050936476946684777115573214252016051709311522733 +5806306154432125296361988853844471579270461201267503074861984238054921903008010783021332277527690547871326604862832931571535476 +6953887201338700880983597387777946758689636635028887005208234910693065403037992011989492126099118102129494636864406808581750418 +8449828487903174265578895543349223421139970247142747092518020699819810023484608128979286359504168816826179123882092074937458466 +8429190917143583035383484835202769619898592015997875633392860989349020465819040052129531463007708159553740489427112502433350259 +0015541288911157030703391088218525575081861192862772160417721389983310098584001488671913042391958271599517521875772879817883670 +8715721723667422477987285388090627370795114420296274720333968016528373376502351404697409435351445641183117232448408900877983999 +8085119600169893659072473239891529571989304915800525658372770589231187553832358134049724486998092398100951195327740893124363904 +6825971614998820258858203237294363381385260403003703481866209020749805588161211696398124406863222077112906818160524737133236376 +4791287439396340287050946270382309108183116290058907007033269789030152170762597022242414148156450382489069745048352057183456698 +7699407910303700018163871163387977417734630895740976320842794125644044206230844211892365927545147294624099819954065586859396532 +3131672188768311610064467750350104984282723151135474654890501043106825950749943883966630006426594607652504022624213316206819824 +2381097970942205624530028578508606408894416081148567879895026246182144217645044116693226312513033539646866882479709987958089969 +8906558060289356651604127285059333055318644242901255378341192868378040612451560944688746385088366187655524363293058496259834503 +6045783943499511329380195312503328218913377235931966010737751685331040259612353101709972563764048165640905281453608759750462107 +2697778235378791219808122722229257971170129904297461026446932149333464400232434190909889478146390656698937276924878791295257123 +2803907707992452534631676946969649948134841465957319668276815925648168701088565169944440306293458637327427324437179025248962792 +4731713616143649458304003973798123909714421863335575637937266819992047643342146589405067966192841049385855050988705354679370809 +5372002945727744156997261317197525282324231196500161732336217784763019914950265863384886596392669097800628799874734609235337063 +0503789478310331174915570738552836640716185310681333898360502850585012405514745303614418697185463616197057663173866523087604838 +0675037169916204490298120588020089851962810662807481941976840354009840553886735946987601156791667665686995459100468477453975696 +6834794049663185275577105102245452812771387897018848470724588835525127031721746222497385677737902506111966509521044773531500357 +5329174285407427305661763207997349560549428910454568831186538339414510256938492615597532037011523362904523476314189986503671291 +2718934812220309630578226399896956440076865897466517017205703733213982937662658920267819470286608114781834913036578565065677437 +0292082835411024840424424503720769844545589088751119682651139453690649901159394940442554457210310520610468908232856172200322893 +2168944494860484191151489757452268440986053558799687398611861491809004454116677153114993411395893222172780575772632409234094898 +0022046237220608888434835342573839194475118566063006131560443847533169495969846042538404623478526559762435571079702487933385421 +2451919192203771937523596048769324041291130793137730493766482430142235410357050813575163787479425148259744336129889104511577631 +4142550759732390528563707063152034344178846568888223418447224601319163578533878251799463243956174732623278426810354689051255154 +3970640202120158350260730117870456696154229567633606514596475612129033212370234978048801831781364493706668262862093481931515716 +6561924140099494620364526936808552193114041143469574838371934269300545161105733392695711211423415123016324534422409169998476243 +2456534483029530345127697481939835893243537886692901492470290039455511175154304300500928667293566968811328935461117311498383274 +7770989718625939764088550940976599710169478296655836026852130278698487150688281647540447530622442916288092929799874814879443957 +9848763764198437026743206591847265528425238321090574928951078930719540533808196681934811602035009442951951047723353414154858302 +5787946077159159168774450433330444862738778849768023490388074911496672265084989382578533753038885946437129868548176233233930301 +8580017107399342108923739552269357825947129658651850931301283981215557531809779053507136238066452267893979878894161383251981836 +9913766525707178278575810543038685579648619267169136094328824697618419625108889858389235878109542078438921757522374605654744287 +4829229647563208502985033637245591132219509835319418276937992741995767258780935271942567981080789563695276467555561800638017448 +1643138233933525491385701115282638884356620775214978523597941747167932566311974729439511528404991306505854174907409034566035713 +6143827997778747391148759316186369744390378766441792907186485522361511087226349625577289271875444080879557771349971812896783921 +8151037464162282243454432112145953184331053916340169687740205103102595400733446898088691425582734935373310066807019088114280263 +8729904943075565576008704186063345542320788733990798433124383054387825974302546390620173375209936756748717665874520884924479185 +9888909645307351789982700065033105216388788398262734159940626037880383673919937047828435841493103609687935677331602519191613414 +8850737384099163846794178899558085730334694261907207404022320796803770107876986360807427894048245601008966306481139963465178636 +9603978263464022093387313902323305847024620528667574592309994157800256182557220046954796119376040712963053063896476690374286989 +3405947975994172973640043088019552492100802303545308377567580689104604977659913539104938989315677959717470803854511462658064563 +3191162645413432233404183050706031855638390349798393321645475424393193065096426473922012746067572812705091078572123464528104522 +5754070330806062132016998542325199869791387417374085617357171312577717425296247007640670351991232446756043415004205160478899347 +1725194535387949369475223998736399458938442651506418282232173272224426998356393475616690357433350683532939697909185050724112276 +1531609961298179460965979011464443408544699180131732903518616680630569852510445519164321486062756903290742218453214665562625462 +4277461038526097907874522581743597670466704734455633690080575913829222964025366450929288253518473640288917518499198469245997685 +7420143434734074307744766434941598412413533864920547945213441905542326713457623437002740516606230037868321117285609425877483514 +5509703411252690324400121230820675240247540075031296148576782911392574907974824839505516956261324207298130826703943230789524466 +6235427271350178310739006810179034776823843871399578101065895786301705049659021548642844679899589499916930305008178597329009228 +8708376738514576473422684695199014327164139367786002985515660277291737951903297112627302613852418677997201436504553518932602363 +9576506417537859055584148204389221076679317163739081159093922729562344633399336320523856409926599959155324451664857304036979342 +7821519375657055506931717396389844553686714012907465431386872822303418480617283652851337727643122589592938283893016029851450741 +4046931470136635022512650864971382510668763279223722442081085741561189482186827034926915081819290103926309450155161557982601266 +4715454308438516658794722092196206636934007267582801367477453071665674490724227142539801920570519231994847446963269786268119916 +9168041401535492095223153393392138627537574429611826175866774713151956729343684934892542983754029148830727112711391973335239513 +6515970901191127073416539618442535739664555006336299291335206114647465786032988393861387837528101930282563813184667131516357399 +6965025115159934457674823672748261872585621777883115554117585206762204650484234255271173958058407125541367807442514328611570278 +4831064230665616198380860322957982656881003885916660535633639193463684058409926839010861838915803748651536627056017889319571392 +2850377483892889368048618651662404196455568541580082178826055899571765132140934322132515590245447090853398672580050380575567268 +3835035801788676880902622563977805200108581423258706942731469718506605351522050771195525463348620059942550507473397926975656075 +8170641249931936194409801838731827726032358290235160889838261562910412201128942409820711534658779929527668900047257962358847821 +3914141889006189906618830872826326907793134446571712010041037077410013305523598603376182060668984447359850821728080727595426748 +0524288363005399115153078597028593878897064055724890623451683531614421839812503951731500983120346087995189240459208148107096125 +4155453406508028056687037448768955586471685200246991147018630064209861444093780513311415335160348800209398873165630411940689888 +6507947702834259046810555837139361937816955993601288388902706347465220128545880169696095175041923698015462911423095653174399855 +3762547022058799188111929405215622052455625552628888943381861262479627747983891009023031983537685246888772136743333720092806316 +1214549690057757944760791464294073960482795674578197576015074847454616235022841464586043262062684847644609779311315421146732973 +8541338437296411555067873828147460205050118794445175546707442427376724688646895066999337376018755680130878200128406968859847082 +9053903102817499105181101245913670467308052365562812081988351351621302283720868904030918069105268441314194378171102376175885834 +7390879175657202991792721024454048282348887367327525251428650799473344325817058656761335824140200255463527106801978883972849023 +9114858631082305511415208423205161099795092979957887353079774012813446208145355233313207283275642795951235302177270570702859527 +9437270466650300884205636449007422609778006468400075137707364599220020468818147412256802671321760977139033664343323597425197602 +0302618076580610803854795974063518981909366239035024103586093127902225268380234904318024257226627138837362185766291678782029207 +3947830739969480621126671162942463986987058328256455824368521897685045168203062702288692030759860685197940546125045250168790201 +0514803654436570370324022741742361761085767189089724981006110323674867393784988265456553747645514420643289677440464150052019123 +5223236088122199447858422156052424410894574207990368063564291905403412016110145065374098953504598451976662453874666981008325424 +8373293703798267046749026333095880042002196112554036817370313956674653924174972808775910245525212215059736970917924397257890308 +2688275927997288192485396746573266295779844438202521819272050705543165472702597009969819708658965256689368204413111379773538064 +5084094239742086467203807611090720704742678798346329233433650521401666169943202745736920597870661200400002701183019652790391816 +9022357095836768187469229301323281507939417769215045474272231522798590708421786910823915726035891376298601448255283206715063088 +7293988737791411397897393109492140546674166871313197493347838700746175919251207836436115320719048184023218823514733017767389387 +7188783211699643174822974868235729901402276203893157987109644622416046761772620967723823455916506713613144614196470266830456333 +3793370001173936142455344055973062003252783611956485218268240522116090384604646513391487197827194186399975132117817107687082668 +3534299426891086746037016872943029880008565862091417500370811850667508670706496943938696062708498042156433801209832903518387378 +3612904603444715041436728628573199366360323396870491816934924984624193337784171063290043144432026274325167439913901462851257791 +8301153338470277907154944290430893973748596163892611069628643761048471308522954471189690031882490684526895423581669049412645780 +9532276724709021001315582137581907800630208033706211952374871095930264465397182584969831143514075753862615083949416688307055905 +8388236378283440896890187881320950399116639874885181013806820276432717798539692296166413239097562927693580121835296266376329989 +7227163697631743317865706478603326970676879690725149096075601615664615649563621319796915072035936935656249230727588009066864898 +4030193127531435008439718127581289588778728561167418433131262502679346011703797827534339742404719888498611315489306325294200918 +0822596041854995877122241367357937629640300673554434225279391124715516842541288461765835730255187409409295964047110843143360218 +8020810762143625695211304403500380226297593451193440072241550883222759127723312388393491431821749019440262383840735943032001195 +8318237479489554100626369597427649553987985282444811016365279010859228891440704149689826185729924642137537603090218575037482996 +5366847590778547088514990246112762636619967032116058526965593868078207507263589910269699995373744003756645473632293041062685541 +9610845056170949836472700450287286628803643135098265884778110832410350875689137416100768678349522724998316556986352522714663266 +1503726130267377258411268772164465860231491482249362263232011498460531343555282835085236658598989040170763967052694908347332552 +9494811816089738912922010509947751572303760145536902984045309394480942411502821839970060527331347541450194445987076121267053844 +0578064800062529101700072602908332148334858119123936457716348054959136175842817084891684097218981249238233949488256020176601607 +8655837084859592004656876506460130727405913211331057771278893395802267144808219629460783237220481671207469404413079770236276202 +2474357024202046544233057042050406320202093782109005795759352430295741395906001878748884723478079164300747108788119563090468194 +9729275772864119080887672349769974091030926766697899488139685369926038289554489717497100438993101868557613700320201349470541544 +9690121476368923069792412981332423045488814436625785624017824621004713254286195984412682695459029135822031307061078889173619865 +7378777371791906660635984844468930766958298884404755753328140351948727806677530013266236008432182713748064589578467419387451399 +5958115863312505525587296099572988051567717080297066945899741469832877228284562546192870097158128338501540449059947967484049806 +1525360297576645372782766370344139731393001666478941675207716176663153356185352097531952067180451056483850877049391450796637643 +0957180541118429393622724140553758348088782996643755097821436674434023381251140872906157549968145359578492278555879752671991774 +6216547228847936682579390485586625009476953902113956539812453553726263850745606267922068236416232274799633682965858910518609181 +9364762411101864870815658017284551581396524871993598349274914322101521556813705161096159781789698091708777511072561306773711005 +9372502462001712636254133123395734859574314940832644465721405982847453658518168263698099694805090947049141539874161892364044592 +2081215406680382103751369362572309567943724594570823605875011736925225634106628852644577321897191396835476563249653975626106030 +4349971249994089684132392481061788229278189139063384564147513903173876133019427848009965641301113400947275878225261721625731279 +0221661121010559617008273964451895647239826707650461656354872916410348912631911913485548662828141859786059545072528125011374221 +4423053386826713852263693496635804173362348029571154806846975163821408623542798104298751000786068795017683260714515841078366993 +8161127671355970070573405532186939626037976009760161070135615665381704277318637283166773287886342633146869956929128780714287331 +4724406673554759228867043031026142833731777584308969100843251135369865682593955020981079390823701606661557011686336153922127671 +6320174341401698137531688064847461308245007114188039688579142931690284251799997677320348583442367762054912291140917964811725137 +8202757825011219494192886746445679726841310275929718330630078072572216660349917135633404881654966136524906313940172149175596901 +4973259052196790674213907468121479716985489021755170094314094085515986880378978689367545392713051595516162518064260512413271805 +1589006020451052603264805726624921702203140540281094471735610296632014298925191276086205769087410850492910589262019474771202876 +5301512438781961299615681939122770744864098845659058825055415852695585895078281739508660406939859772275150311279745599614771126 +4288153404604175490400855789649207035885124038840820254085618553577028061229192741635233861987354455133991517534727391845052478 +6258955556704090814031626148938140929500657324155298474719121746871071846048890688770977276676240866370191127535467056926311394 +9986426460698287989606477786579619338308357811140009068302509875335795457387693847614672598016435455104290716649294340111672025 +5987575195605597807571132149058303080863960035941811196802177359118666488648716460574884370433012992324776906424681910784438914 +3212828929093079665577584834614675181147027766122500255332258719353578136517199306847607401381636227698622152320466280962873067 +4298697726297500762842637031315949284066464528353792900058970168170276550530718742305480231641988165044232612666352048235992340 +6528595274852601267138855685497658048124785482783676783853867479073934060681129115971059226209525138395957477851256838662435594 +6333253678417649760854615241319608105505866305579272971991684020242948061790638726814427247631657265086447512599362760504729659 +0050552281101516551597310571530814588813676939570968218933911952205870242379775297989531959401920856446210979962385790387518282 +8767289958783485919140054170755933223361121752964753574235200401049015889601531517259131960751322512615901642287582440268512260 +3451721805339224609531183820195682695971871204396323817335046115539455320833063731322682287797699412707522241064736997253801427 +9838095438421652946093122030638044112929662006537527680294782737078006325003341225082530500978086841673076833294743067222781678 +9343880589742192946487883613246357871516859764774916328816004546765194900049207055237803648799342279688804719689248327413730472 +0721971662815930264205326239766135366858450956630362245585637639859898001993676716328053273905567456704608882800064784850223773 +0571787432542029050900323916790197508546510517976343882254348928206749045986326975042209032481347983325984442399998495068210198 +6252008138898607709468342620809002496223471138515742069863151888069692871096460617937300581107228988473399264367167623204729419 +2797770513682558101443641688720044042871553905410373246365721075390692021825457052959920292945148726156660506042073326859993576 +9943199450035463537495910614331149365062244685626306093678223057245690588586113869249388996930139917906887631260478153521760102 +1950460426985566320913113822097771379876492753786278737646554639417059453209917138732302032256017931681335738155323404263671794 +2838332702415027792861677040542052438018840283865010592299818970494138350530560217854544968425238656792859418734080761867272852 +6010463746369529626569768506440616846423788144981864845682008740038225629815725166282753185272670640607060429451038753678650440 +3603428316928538153550803942955541314085414603075220414550257874931378714081625009020301955524753074400211685480928675879412454 +9843069053870715332522840750526561812419932672015609424516373833579891793837439080150576601064726524016745608185730860593580037 +4790338120349419657809887950279987231778879709683733319593125286240898651454362025515251496405484678061036071464115462928635106 +4812436174990135339020055889098969606774850114068102256726271424987296420443648067709292253692100724695627779636510134753572039 +8737165463831035630144985978697481580718872897801411679391604620458063323095158841009595789901639579062186093555920948550517282 +6684194497843892923374409974962663409557384721186689597945008117535697913915795350771799536776499659499378233933827449930665824 +7081582116471192658574426394048422901610749624872891986363877792779389085331435286663561611309448994925468965045010492951543901 +3680646340800173339218788077063204059241257097481947777145409084202800888856762009180382127277534304230302616826742040805136435 +8971885064485253090204122142512764196490013452340872579790701372873179615012971724582237981895972101835369658929642000295179467 +3378697466160221993719637822345980287088027594206423215053076460092623235649859970956549802453476198188280999027211336510921505 +2652711751748836279015697510746966396333400935969997511167766407036252126460465825405459671454621493581082715193532241502957665 +0853017906034458377960386056517204258262136080721053029202866891152909200067791240221407899628896204792320935757942824137109755 +1860549007099165197284140872678756594535459353075535851412365063078974696027557562375802944288504306767016261985686187927040312 +6976439902255838121317145194266076485068757990450059106045155783172858632535344129113251970702093415466811410640476211193415639 +0223864295074452283847305560600110714424512989818888930888094361164360320952698546375108408821282297940103191521862113682311023 +1062349856533324628662077342594233105268417141054370232872638557841382516201012169119621084906023902818960772903834214834404456 +6774964345965594961633616650657071025352025214474328041017041037485163730502274877992356272868337521422636796698369213270452116 +3863759791559426852045781221052728648632252466126078672480213084512794723010528131976170544126507023866299598547784848361822975 +6029193859923289086357438220756059069241393076481467455226491973473927966812070493726378405565631775752820893408930243604143389 +8322645429369121872596731546622494472770504292226778137537611049076304139170096181584862462563435300455143882748092410271989379 +5326365807733260201580801629433084263428632745627040818781791453399164548163815232430786017320082301681273560908524949863305250 +0127708812120756378948747377847983639315742195284498126386446424753766099623540522826797908737477298768890173160498925727449025 +2938525448223494002554985481215274772014195861665818544594199880542548809977177276693721985173829796357491362226406805154130568 +8969218337326188250536163628581836759650127208671515917790854163363077352794417610106050375787449708519147175922505361662892084 +9818570904194161360123724397774706465600642465735373239577049512865171271762814912171304543676985961805640424586977875904966200 +5504575938770959529220023577243022588499122259144158193746005510614090936240126010183628014128216077152330048541538274583951558 +1040854914239994126791549056393295346381943572230033663200164558894293446713116407420635913349055999596494259230893231105260703 +2206196014061006612340848198814409910794456229071521204012047591463330479752534343609457402515951279901699854399989958952429475 +7726196843265136180636204555752895156585913253117092477574090168474904085406088453119889514918675712220848883891583790692329162 +2932979781809321279966736276684170473868093894960203921581294390763273096543783962941941740885305313552176422059326344349963187 +7716303606885535604492911546074803956458411462697046763121884171908019143640932002882802971285171891988638807617682091180833424 +6415461043036191035529625558425951185907448251833755623616264167570352631169542482639741527208649286390063877760359771911261699 +2470130258381358062929085215957585938262216089039199788226601264737025509814505817029779107756588940453410080897136093992799084 +8707632659399021754985431789449236853018112947892026429638184953314257900880019675773825490255337783864219014358786450842397851 +5533932851049276649253727477601106534672571299541307151668502286973847937896722437084410916259029864044870523541806218077954614 +7765554877085157744714903505492619912731173476480412564678818534684847115861709661320561820994876787782948571629974821128726525 +4958648468242228861341995358858962307381366972784922066788447146487993304134477988625345942386711833780639553271598395688961584 +1195610154724201767373227499183087873970044260009821201260837039944092568903479517438262374212926002997007192595185854104973828 +1619364472726153539242937799692428699593667255284692675884347709702639988899768923848970457805127213116307471608615036594270036 +9983245059851562634241554611084024908123182475839572134087792577459483664336562817481606414919540980247553055009390347181343478 +6678203813070191242843917525205623295811411258351922528980269188922471356248950351834938277372740881478275242174992792567496074 +4143356634732380642139347869145766266914672583108623737574556686979737703412637109328384991661662700191639112734811399558605096 +1208518184191671627470570937029456222980556566946874634805220009038965108642318793927711482497435062563764442708954406150308502 +9023312095876743701169506435873830799968604505754154998579861924311377960500475654542205202570979796896262463835446621504771900 +1307938351217139321342488304983730107947723886674861520928741265078308049241789485663045316829261201640722069914290181463616514 +3662774552959414495906707805907185396346911240417542898771207054497319717662592108767592192807181086654802759806348618578837674 +1314737004679848142363776160351558775692327407524329957179113590512266999745499877585915362625510835561292623778541138597816039 +0237557760056059081432345998516737302912533244471786164779569231630397999149258259824248258872090314272651220971111732309368883 +6627477978424025488072609778273019056422203214910145966781125727030515618065001832071393756745595983343475497495207623260839724 +3342966216908293442342817978848639488604191273604917723840719378411000753792919755939336669416561658839344042868764649850515740 +4843734023345647692176295441180249482639581287413972097837107583698158034620661397371501344888180961705583629809207419852455696 +4443380077789857586353070023335893700728665808003439355680624696628418230215872552816714310195766329481088338871468994321119252 +9699439754749964974015300163681954912050318298048480045612220669380962262620582805353459791375378214164628533298845978013550599 +5105714350835688155081910384711576767489399113130569825483778697383765314872304014753426304269107388256780976698871683305123430 +7876749639965180153007689268468639912304158853044482532644498399416286758021429281245070574658924487014301816203665832428101533 +0785808281226435244861112112960701400035860271883263300268756398080488577467688265527817734331234380039587960227981962626600939 +3364281430281513574894870100381156896475296131097244720576919647275082387947394192830802845871083057512101147458345259973497437 +2352548478260815071941762637203003924467368412816659772061510861905396984310747338352315940227937768901334141297190271981978620 +7421497155149250772882271160825127041610751435166048440557936723442915599683580765855387190581730147042311189334715395286473607 +9469405090355283185848987802133089556363626275612421572132540664555746805117424373802421917096772214790891162942671883468819382 +1589417274385691408452755936967864047060787328586473526198572660582234669906554955441129992972950187613542002387015080229290084 +6570356377915672505761938151404215475617807815543585573009407518371861507851745477702147309937407690084987324419699405440825645 +0612011361559116532371585728822626979903698606786712794256913394582020905043090461211508518273342883931278463866340432838401156 +6760882395086214518227285005993629709889190889508417455761844997600135276044719721514847763212602718036483323892756763864051034 +9188515996754462022639889466629680669832215175040501200558563344456278764716592388326318156786971890684957626542747491478209927 +8987779229113917038185012511339563267826387201889188999106457112243660191988731669456871109523244231764354533555547239773405699 +6489667993193399907320056380302219372587627376810666191791189051092938301013845696380002825207303265588504074647639636023424359 +5972299897263342080767319345606033144822159869746010125714876643532020183420706758750193523599953077555495726481141414611132824 +5084488926501974380578938135730935789462111699625550813110845128956753975680460172778115855586076226024263960948204752675321380 +2481196609759725214010722486828189087368975741750919937794099496934509151874334791821893377695611203497436421390537165843568949 +5731231776297026471145248406339411301301972034255825750883890535679456779763247607668575953175369910078981695492172415999716775 +4579188403021146726016160128342968838235818738719371073259844121400672340573224465598262036627730489472180977859344538916635018 +4072803135751982897056486221080699066662076832459383392956264311706786163956015331215770418998634239350534320417157397046765970 +1201062381226118587752910736476250954770728984407318744902312031409265281863798225473350200683876473994417794886956931818743554 +4970302735301544243501924330552682090042743013078749961722848840311456198559231934700094783625178641374367971636230883796585194 +7528520099930871600504738859526542685454459947737830229008160696355714649737859829804718726706633142812661022345510571151124118 +8421496990755255433515708880870288195255467739826209142506221198099596830401844825962275951060431553735295340571732198670724017 +8936767354220444343191661460546264683941090169583805460925893859976635128662736335459378982086771782291262296836018682528094985 +2091822408378038044344571653062122443897076701230437493508375904957950547708046448886825832044918141237713293307448531246301325 +3809577056439308352524023850120731400555497421961772257434416551729487887916281181723448155709611102047050048200739035199609339 +3691135325949817192195090067443211343266627775946016712851865749742799362777412328641698096396095788341581675313236780103509391 +7728571205866997785438131913054881617576104163904406501238204339704104937888486853321561284077020503299289883355914615218277880 +1111888880906892863133264981628224969781528602979570581183128605546822078255116005933260161155631478832277334536041066268750461 +0079412603436706091984459336642210703864450821510237108480924013766858888811236465849216492858539373276328300134023520401083981 +9032711135936968864615808603415924938529404508326233015998723558019026312135753805702013665081865026922455487474085399902382380 +1925270181168851392094205156290625337733809503843149282248914199479920984244708969137863994130437975788618973591803506088013062 +5670224338144189138135191184201868809626683864671529567267492650290228767102161777058656146469480714765930525720049680179040530 +7230690280352425636146176530879993888167423730080287061127174186402752375468464868170738182852209838339454679149339529988407952 +5792512066685362806154726245973930382853685512688261188341000372263049656702777229779863118583825044769296407053427341340382043 +9064288699746230367766305172967912478546347888349770026780079432939827599619309710342120385387650986045115258506762134679333064 +0440596074705090759141142692292577561658123037036520266673482910698027486128518652316613594364723726387332531338654819095216165 +6586405699035556181676379611570196812319147084489164717015394853295535428378692362698265567241807453037131148384149957093387466 +9613627657031243653069021006489437859522461371334813124213550061172753326963975967858093098270917873153645156857962187738174651 +1136920419639517807349446907609250529925657860858085930986434051082566457439789550530915328824048952555143480521491002977432620 +9723285190605612751574356382595987410097040493290287438645066768564593720132821343589116869402530302205558307160327389977244390 +9364492329019171456987187525681281472110463501074399197748223709921114091308897093844593961700969890959904837552334397994454560 +3052838056301761186571362672662664380662089189821373489289767638693120179754848616628936578638481820222476538767896516701765186 +5650968841999476243265351662185220984505585094177664675790716289996388896832182868460012463094065416068468660526498381293391126 +9133890615055659775962704691788335028541094043193982847019337250184246131745395473435608325229593796115617156815138186553076713 +2129458246410892733861841029801352092648607760367150219892460311630359921628868730343711772190656001417014669173768985351029224 +1665772830534776231474157241609092342166046079774561656411248463883336203142778691223352454530469529359343352214726777533890124 +8331713345182730719576279806483123633260411891324365421657390996528436543606816818186451009421810586108827795803801756029033502 +1364037207944391495829014284717167206349222584053304291053816472070825719135329033244226893785498596526306122826589748158594087 +5166290726387661882217520391175991236803942524737711932725364262567914513184343152964650814367428507369519194246642124353850820 +0827084375654733169383428093676562471650179031999648254047083978351359301318312364388282400723012984805620892215650029052432539 +3941456090006715583530315193770635520541951076921761324088412023106545476103418543330517872032796586561611105128147089091776975 +1738857734263197422168561159954861725900337109733462635773133902960240600973009046170223803445433108730659547191448827977753217 +2930699447354813297267803165810088809944869393780407968089194813574507757841359950100327174854511476594508709317805723583889020 +7143123510044372032973820765560757174132206252079849711539375893206623268969901549499791778646394614259993009419795504303081399 +1805751816651443729216778302694093259724338799262345264750249439983832664270852123252647682716640466581767974713902148881103321 +8726470726559295990593930464431140413396151555993515614244807290113117258035592083195115356107951148992883803789239385817695980 +3921614672869622892796645795274584202330027636891305986908354816884938844639467554178364479042973665199423978535726526806853994 +1879696934764137925030122852767473404168885624623810742903886724883175975872670995314600387454933128918537822599476294544895031 +6035100939350635436051361083289099777664034736094529995818610825799950536521416106176327389907566363175789821848187634785837579 +9000534727303607788183270598866205796784604375642000912263136681354291802540390456321071569535579301082240092546052754082125140 +1036718094258942656960699558474884940333329012199962569808690163604756147670835128428656230386743308384472157735698077816035891 +6071364091704432824311172611510490775020204802190669441798310247002139209451209194509056421614671635249279135552841468497623698 +4388935023201010068076632458613446034376062184128689428896319432854524321580911853120662708261911385263651607882193115809188251 +7169742597495615052582018265793684790742777650121858370707225197495242787747124696863007018741360971574027831332907655096436810 +0422401456149077117264566129183248282220685815602963735110673297177754943137791219212563311879171990788150209340116758159767140 +2965862835354985730344339045722772376955873793360410786309094363480176270871774839639924049136780870709007238189898961666868275 +2861817902533679195525000340605947416733205573982037034681250485524002724047643727596821058644083422061496633576063860945664624 +2693808963859011255454082437160794171986401169181404558686995397058453911479501396355062702756373669284391335723793677898504528 +7653550559325583799147678302921854984957213549656349312970370601725315750255349048113259251600824306253788257181726835044331139 +2669198588461532129599972595504488308041821964826043635029559596161652526633086414778498340817442875389328897830361220046753460 +2464008866849845585427596306460289186014718930150794200485697147420894155928351507966633760981788686260275637265508592186109230 +8485080800308772606343366155948976547130610312843499421865489766584635822710314441085925235277117540126323440360094147819765072 +3284154124684872982729898726330863362356401302174583854830959819131037571458234265917476543549149369136036055331113709805046443 +6448518117737425864310082406263081225358862769781154236900286043306718901307704827192440877547054996291784365092391305036785917 +6937932081779624691771675203207782587255392167526245881985222450132700822827458180664476166098376639952639111122762216172733126 +4351428226863320079499951081487882664115198758192473811033557945313783704183329712942802900894464150489698398293843181571708391 +4008673285830233165375165239225902635382169451174378889470574573298752951780171507430341658864697744996790183195739971709305547 +3512291497844318467447674406573948587271898370849211096489030445902905335005315909627516994616160913716819045790842998532630215 +5522629798725110629827671497041697233370597035945650285606531591280806838835367751631589755667302233305861288279338186531242009 +4038072792258238775154639697267095650759428219766235775721809805667537846787229698544718121429627243715285966487077144477541907 +5934583620628679989854657650575271470278836721346368661646073065566282621355114101575479793022577251192819889432201758294108462 +4480564984351469882094948004921988867959440174254223793056014056869119031094103106976186436427858310194322489736576393894678612 +6908132113303990201784965196474475898344897609537622286517498106001289334026874927830753197509682053509801560482547631691338408 +9319954675495203754314132567507992211027517777636008035159464490278748417535817989110579370702464528694578198640300697978306801 +4761479515872183180982458008106034482409223233605055754850233921649363735633967021409784641062684672780275455212963103609820201 +2564622328254102204752883685136476152747302286902963024211546476863845751873654900973840371931652957066788857238841473470464835 +0397866053032166080589875219701391296400993039583315649582052801128849941725263617220137826074241870851980225112051857130648469 +8173011294494728142605327204039773210726054522101869637370867240485919811250566145341338527471719086407653593120324797075262800 +9276563059829040598662749592867289249306923572562392127787551507493862417784430100714677869547866344355462003505196714057035937 +3281114518664069193538073304543737277406653201799960331850340753228936196208065150992985287938425817855366760648223669372651097 +2101840795886669078503855800666686104431608113029414404488344962534581805317909553125776693807861855300451192405993773968098583 +6961316934095858314386858284332606626453618369372169846037717301254036577518610498824984225850477019844523165578520651885370736 +5674574044356298030636615794164061879134353864364329335297710010416084563498871752753647964277918366926080825456181880254954413 +1539836063070409136278143569348758593204080100329208721008698517483741296128676983168524689365145284941105915339931290444395066 +5870435438526474510831874542069716476806976094126759545431524918894521691790318139213265279009865120135936482961615836310836321 +2764563926650270576320235999861005882574146665848294584476765322933617549606955812826398821817999980232276039438569048353472002 +4681622264311366983045853037646736891614380849223091345272419576321258005567715693518336948951455962926248349277346106557405718 +8956877856487148384136234859130353374642270367915456930125213227196134161996004529594088595345226945903651779962975528996861250 +8929969605933523152819203271238663371252277051363612117118809528465712440154903065164514629518743371367297353604095710696093004 +7157581534746462952501632250334837099070591121491848742704449306300549604064489388204805910975177833999231196480535164260229721 +0485019965651431731437912577838502867419408108382589212055345268265256532718527981331506279373063176342764821347791812231845829 +7209745175723014456091749043194092299024100594164185069424964912279120968460389124726898963864775282217153874785467678332297469 +6065725443439620053836394925815928161931638271242410865193688681337289422879252019985383178533022928491349413174307090957339832 +4700128875271375466912640968373319232662860319940601106545249660353269122104137983304514576260940899048020820371598690778056227 +7730285573414592054013879878236163258148114953801223831916249666510471398921760574945529246282567489263612670937049902168944246 +9130285817898813367964618316152272977147568779205131506356566544347370722993791917334237866531951886887339933938523558239665651 +6324176517408834364349803908520408107012550850560156082064698531696664057881361151779666150135225685603149038329558618588277296 +8952307375129199935262885586527056730554419885668513403462367686079193825971303716999017662160537275706617381132201851911575059 +9097939471326158030329763386527360778246814576714519406324120582675890884898234797792200042538307916074737348191079713801271431 +3543558395840050080153435933135880595590305720081325097084551423268599148459068228435603649060705649929053840511309887473304515 +9764838954490408335606543556024437767253488401288444187782685927977837265995938637882104106333802927203512151240662538609957779 +2886694819124240943375973107339251767858923004989669776842476259662336587537006376705925903350515688363082070599436191295014286 +4373652465925317661858189411340138062597385913063993168012584788091469995881494867908911062927314918797351706279530078724029778 +2064997451678388426343449295809472909654204980492286328038224332702407992526584281413262450071832225709127884717383554674100721 +7539736814358108443787011442956856478411159063433502735600777534624427911961386455371172130290891500709946519469882478498227746 +6726050228227536316385485897543745820479149980410361188284581130022635250330849613983713225572081803265842903422696226592604564 +7194667876267325769087238008916325523655281922437602110657822708321617107090571364990182422106391457560890252879023326941272951 +8084067244599983014933199534424787256031530896533024441147175887371345390886210891424383817784357384520863399170708018540336261 +6756491974545573085147283999623619067108143004190487704098038956692693217789059190015343978239812814528286370354978428552071154 +7449780712548692282752907313327411787266027754878897309843460775599392567392973717717866266635813723308899529146352109375925165 +4589899290541494280337702591008141258629373350544520604291218705671399173981123075630121451176103241759370092474254377359262199 +6674505740657078583031897307527013973936077370107324719073133704686647880878285508608449940595668407790459295575945823755660650 +4398342537751131171214988271811959189579782716426212830903406974442463464257365397952526980356187741927953055943122410685033921 +0094907389935032343867848721405008172219352354700721989014087040463745373039712419507660947640188922848825854691917677041514246 +1508958586684510982818262867790308592204381251380001158886369026776930843982561684403451614590888186936500388011892045824866501 +5793657116945434882250299286723935630635472763043771513002492162136250578168782159984593375232995341440779322489270838200480406 +1528263927376996749090311110890532617677357745138138771708962614857985706783193215918275341078100860497124422089882672715412665 +8047862045380193284671628316345513578527766132655289631199372572297400350940186548315099505461109159626804827405877434015762079 +4497649985437114106619275583585240709212568170308750996103273375973255643809512987167799126855604592582070720993954457629607433 +4115202807426887388976298359856110123402007311986746450130132490988863002251413157990023529650615874660044346489668889392287152 +6927862701563682169262409452072639909156413247108366389659864970583248090206725794860315733927719571533833830245401473668394282 +6642155549542044431836970672852504846515539364245490047433571110732970388069323446548403285359639096781185191635964711812010789 +3758505500262572544806847683378518452890545811878275350915244958015902281380798955282380990829046266914505428380626717794710699 +8543173740869008063671194651581188030729498726094049008714991031352383511909957936902571701241692397622133551499445586152966454 +8558201709766887626106558682087493174247508346751479027758498057382070135663052096489680200478789301589881335174285636803583368 +4021046412548985259286576563116974009718144433360698911802741759233868394233863822972297994024437477811572624976113883122932672 +5381918891212001167216621281602456221735226414991801854696667000136111863626404844152191631832767778925457352443210000412625595 +5545489652781686362831418324972755141214447870296324256972282335912548489681306938005662008761939913552064594595349128397481698 +1089524707879456280414931183450523776892076608328015438392554074880142678157014194242067164456688702421957600153913700978618426 +8305117650117364060258714900356625435051024551440455926273115470939757929013176898347131589373525848240533670222323888613399302 +8471548092796793462446437655496090111004563242834114582368336961549825665258690557364306703617109594457055331363141186121145408 +2838670852038846380823335210551520468349670239701607224866707358833084563155287946007354912983177147283657585469121005219622389 +5789402261399916318681167359698620197070246205566424633235135391148334480479925501535906551354569207810065959398048065712057919 +7261986944279592176600370192037825062903228830667505762121478097517284917101606104242575975523312739096999493662697366954853560 +9155879998783130488358931786153797296783212124287955652349632245457365996542148509944356865191456587886152913113892875578626974 +9514039241881963923397516312505696923391431643577404694192615351139994947842245479233801981061807552927418310904314043203688694 +4614887134561403862531573523675942563015366589167507205886559178959551755768826801165790017789960028672209846259757000436195062 +6712756378314975481834508159845487505561014170818191973740730433116802799842074666829329785272494733308089058087919052323067808 +4343294134478763284253795500389053885558601278822120237277266312668433120502431917455874080753529426382802888951317455320771924 +2376278290758786366787499337556544479743289945243171688237218372051181305516529407353834157305915645651032848895215696121925178 +3277461207520288197080048258823569914990769314412444872094153856107817656040287107340131396038173497845042121242427193089822818 +0292406985223320173089591545488895576256166382750650552107291481212988120961054843360076945546651513119822979777064017606102333 +4155585975612603215642148373485509667928447809166188682093526614287990594479841858693261264552895318696996233565563963262273017 +3435051958110889869116245172555488932493678104217964596210912258968214519860568648927109250277169274770553939366194092044056400 +5580589234840522431968399852060022338678890148720329696753155804394492049184529967776995091977565256891476404577309506725891693 +7335500310521157522656023934693183042792687707804385279531540473816692277059506917018062802407212114266734977379325447178574076 +1771613660964024539818471437377426571013833744798577093945387086328616540439686380471290881092187502982926867793090143460960995 +2854310614938011180157233734163443946405910506035197343508210254223120818772340019254546366421372519708012585990732220553479817 +1257049653581939929259306981138171825335308854774522129783756061113569283775667916315356572397505175426434698580681752134340583 +0335808023777690523056395860773496290527877864032944037336174082899748170635967157534100283308552217210895819668943595660390141 +0793041940236886441234025927232106929006722186357450970274155143635586525537995503752589941375247670685666954666533793877981889 +5778831921482018016340449172209749049865029154730502990831586948434948616688177219302113345681124510479338999307531273610240254 +0006855309162866716967524960032543880915882390415328777432560766307982993890466192334347172872288555702927609941028091228916549 +7852480060739012387300892561223523799609034777867042713542472004721462831725545348869852796577495340595446292276187383478785958 +7959977096587753530877434044450370650017598273016702141156049535064683872702373865885204993323178473693440316927580843350832264 +5711255434828429627433858416873855046302807982520306738509536092504233877047237492422270856933123651986965389822895956478482440 +6861826464075028721520622101929991628946998804166738863536077306024363695385475918930843524639833318710346078946645157384213109 +3481268732060383732986016495680958177849607099558634081654518008026716403239770966732475944721762791451888118885285069414233963 +8027947110287197529845522807009685886470194904262452476975443162530080660974564750582146126181908103247560999247950332233807322 +7124796294345362297022824568300955114164662928041497399401450052218863148267186902693402640989732333727359655944286809966607247 +6673207022195790654496166749745020173181219341922393697843282922354805033819231485054350962234180921218558256719281039871046137 +9465616068877625982160091084356867655695114307535279252535857794074660077470905649500203254845487633889884403918357791957962609 +5439735864176545493786682348080515776951063552894497267405045766639566000474520462761676673185451169867195127044329565448901474 +4077128683528317505947720920671307017612997849640361880751590889728513950754717072470243203246494346250198972938371059265537757 +1744350487793132663117639969909905883135140378094636348689423577493817809958662291757247314354678873465246383300244542531431911 +4940086036767442787655065360411221974038277063722727918083568388107313711402640446340277014461054034344740215045925518041320283 +6681118829208967331321877862087707476476072276521025702668676229506021840555589912885589031283416146794110309804988142636941186 +2405582757418961820523544833021078187732693088350227346014916968640935524755515218365262141583526578487828351593275991676198367 +2826944841325179084380122395615301242526065149184196022148551459509194360014728846066978997793718470140240650758184827065285092 +2993193263125920746329597500608467852384071869621346018318097029329594882972558366328973665134862355812606060309541457130668022 +4276324020092753413044519728088179483413580580809338027992824022071273342649903541028827269643668077697566268171224024282173079 +3298668219738409681902878367215423318085688425654045440812819269643490157963034537370434474347979142141704582114328144364074548 +3331207723416487218945222041751113899922244456772815194220564328760009807415143846017951460454941724943113249362066483407691380 +6952116171208702291147492954583173797003314720166450613568401114973391179929136073418264108322111138297086137222772204563421106 +7600975435069311874768177860666996344740802287521757586619300995824931951698677022696322801985871429773970218124919477206966901 +3780912628061324155929062282119787356103921261553217430966183645130255649596951891774470507549609157651291460007364039518971745 +3378164561008341242097804088752883493567850412617992500593780122329644848355808639540910546811625908951376798420135130564695711 +1010690664052506424047390590878204998037797666671722315517689809912733797153349697357764209138638827429140983606619452818262145 +9300376547277559089699722018353829113301128665766029981150643108015654465357713345760938255089553063389045170395343438086345129 +5388574617722177598048137212627225252667251794017646238607267836072085293351506292982031628145496634572585511875570432312724337 +2949102931084425964911707872142235444671368657598878661624977383342922503912907622387561510392479663939836372865380891249214191 +4125450850629924708967370485579795410196136664949644795471576226816969360966521409006096786481214505471016659590219454123795063 +1052876846080196429866636468392224678653280363636952881423630013978399548705549755495939234226751546533383899311389616571394812 +4742555980388054447437326569075723750771475897619661025765632486508214470920970384046022812610698865095434736181621465694089157 +8053089116717921661140443537567547341019450706571788438437303262785839761518407332728972316994708287799676864196511854612689905 +9729609228443111962917503361177873795233721041675657438816948255161968110343038443890530223545306466481706693430767571885086143 +5688308264781028505036247702821583514666470671292172916350280224873269657299593653174145005772915708578064978186214186153683267 +2204744465132776025498386899741825318473763441099667987231878067919021578886769657107832299974638025812458213339029056421384587 +2941505567992035598339903647769230684834003209355669548196807243023989038563917277881744820709582631259852618961087166384306292 +8258024223124059927265905619693379087206675705618331236606891852566067360854244728445479329673501981619440816785961582356678690 +6012067901366243313604096658402361049393912691496270752442176761357212025454418262374116671287518874273805796136109140975751742 +1773101052368269774971707329316287503544657412042811119480548137588711677240886794469483314479010931714923252443537409707971115 +6673163431438354320152064250744844641052080216051967752388682876816416999627859168211126178210239748498710723469312339914116142 +5806996877521195102255361656866436538966669931939141317697197556942791721788110932577145668299881351690980272915197702021298156 +3837290410389303517128116353347856259353484969554892124616182205192313287433755106122400179494690010200326668121477882253979202 +4517266820218197081941589722796747209204288657893693887244618207431797169277035145695561271253339157663193312460041285285239201 +9619778362364427885644889878069243475033064627573715740274290928496163757241181156603865800841856421952950186816149922823346976 +2573799739461267334017539893388809186322465134575561495883735717608135099991766507451755547789018617434096932254961072365198882 +5105319049917763260343536285268328841500875349287045804710455434303605043562842410645690374572514107603618866501621709959657187 +7151556358315357415841531703618215410583915203575998268810030381640117079961254114904804786046065507943854733690644791284850608 +8204760879007736798651157366669090166543821998311590636584404376075249452086719646757979910467231964531015916233682304625227803 +7147573127581292166990486442215222962358403851938571275150035153036061725108691622876773795056511233146161151053768550862751540 +6784559304983902886590174698622574195988266824149602039534916660889368814614010342767085921580402597291566274193343471685906410 +7880113249001482900851888199979393787162065292926067710104923584703156779327643300580458887132435255065678627456912854660490039 +1350749751135031413626228320025567623412283741939627729115634082906593503710519533248918652855496972010878915554543211009367225 +0004322455171197190670578109680045148889288778920452116958832375230435697820421502377733900228528866659062527967209953101213734 +2911494504322931801753348544419729543749078880072226980969166346711819931480214331433779640233877327672317049984114641749562870 +2396528513460618041259531318301991286281693049268735199694175803321673323893916691134629842475605991446026813330356086230228330 +8981164060702124227875449453939249569083280933393885800497807219622718057632147355043854391102180378987437281964171835261740700 +4207084959173122310540889440071390175078165835125695056930143838697501277302913507401666310496334451392319525169760616015349520 +1210081234448921940981378280200458404260945592669718748037205896059223823726981351910671794263375996276697272684781727338912271 +0029168415827572351727213122061796310780992187543596747141643738703113178475108090159020344233281790182332311601803329178285993 +3777296404075934053174216751857062118469565443797038517380817156541529651488369770373411767683949249420334108704951789531604654 +0677478972172417855596910438873967997191564526032435944553932413671467038312980120163881839182106666575810117448701361229089668 +2906070251543291085920125260549397785656115356661440571177943054793731483489419102892567240649669779614310761600563474068066656 +8852394653036692892038076417261650066148179764783605637155006560905389777668215992491194523484007340075319390767759607804494192 +5467138831557823428775541689197797741887498255851810682591799669249141593216801183262480761866825456541084229589715596536290776 +7751352765591319462216158866914539056239902442050723154683435064019845231969816008347645334022334350198037412040594819924724593 +3021915992015948439763471750840891829476672656242373621059864369709314486194930162083489346397306741461969474457136306652660884 +3810149798323432950565012468358334053474435740399651150333513841619654716521117976746459459286758520929133377210685994781661011 +4783431510440557379639204082385220913253340902177656244399207972984745234341006126872839573292250089207868222847639289722379753 +7370438575710728204326451494061012215276912157251877502908542289047363288154325283991522374002173371912625950441590135539908011 +3066350412780717315157416276543477507139338679594873856557881443518665559120844101586947952288770461228629756299270909156776058 +0225437513080408599402290104732039265518603222598728765215485627157659513804399230415740373980117223389491501554877516067224969 +6906447098674865317783282853291741419024882827886344308680586123738306172623618313185104401579759220967002703474556768483264969 +7905688354362418737693115373990150326675244455866285863285017259126406290824772728738190085605025791061658351412473817132182003 +0647939692176384124068167344966453433916156302209336971942526032217331539621915101096729732974008611758025821638418841239335586 +8207612011209373495228760846993905535758093646702045474203894639020884032055233069612162464709714659219943117947340234145019206 +6685510574753704984128082407027684573335104766797904438216667655851315902097650094638260466258758906563426639941626773728838182 +2859725751851113943873234212381842264933438665075493160334098268584252964692288511136721035397358821669241681360772178239556283 +5410817225831141636647896001039576920297438613631278187702231335198482845481821124632157811564428311298381077638390915142518664 +5603082383902365441423743060726658870177085955695406252400018823076710944733837738608757044351867174051742821082475300834328708 +8478410686281696423470828758415498795393164508886616098722313596122431017415306773664281061410625509783895380848928242909573156 +1195618788186485122683528679683670597289650244657656876407897279500115335276331331011520690820997358881740005917663903217514124 +1469313024861648921401247135353735958071127626605430535836717325791385228578496115248025350543355198105506645012545576867598396 +4272483837056013890177205566878534986867897273483516131338967706119399325076036529394304714161849476343886942682280685872295940 +6971223873405564611431588388181753597862284579604627783908270991876509853907693857394999066866716324481756490631331601939564088 +8692753456851589867469578350330459046916706027508903379244973723114291207510039578602992677933126396343784500936297228340222600 +3596764762533798756045077959160866048043299983905049607435425529900440769757414376919342785743646389458775443875643589233861301 +9939277922617044929198368291152778217422406940588205287822196961674791811253436361915620783243520085430696637802610907731339802 +9987990017965498772438500667174890865212624587022291536612477503056361052627799082687991851074848222861818981269823302761583997 +8147838382210431535807715103935094271794068054691587562868334683152631170314268604516246803968415953907592500669227061338962852 +5229815249263579089803694020966160885001383014770540554119854860763174611534378849807683267399405523325826175066050422465261245 +3718808810539902175097435162693814660278935973946526145133557384972950132995651178561319774914749271123710684959722970432994100 +9959643168956842935132972702456973644474462349184166563719137832114600377784944150813295076399096859538689536653676932352320454 +3562313742471254053603374767998733448940422800590796399248016870732131873285978468485675936836421845482343165519443520207502472 +6212070810123317420939860915076451492474105989853395883410582273920108061505858814970010182505273303507369510379631126623445635 +0255217936242316574157324758578917396184805700986741727460755136645033816635143639164644674079087020280816581372010788765004026 +8089847944137656769780273194285160566134507215012061013579883469551314461311271302478380193931275275686279445049118291696625976 +5717365181365376852569097535965171977412985242766246364289767242179344339616335652390309582781287529277190209039953388689399517 +7041440160561255381091312862643936475613988744385378977312543669992877553324112750381702041222192086102659343479767405018249915 +2110051683946677346707980171411807954606467103478530131043035978033107947826868347943468663705645386376022529329242653151632937 +3379574154524802337331393517730441276946707038997138141465007355281465799683250899916025487713716291942082244114027843952458642 +5030312724644064165890328712593254449923448331621966613833157539215136234997090424298061395610381815885285290455631522924868317 +4994969373293084869354149476773798201740005462072247291096910619119410509333790774262072570461057954016819195195682237016634823 +4014617100181465701775054953697719420102573860376968588430229723536957418028396409863878695628364581200214130204435051219695552 +9995923662150908682939564279380158287700441111607726222291472481672861788611478655050339588555615320984584521234687823576281254 +7451463346941792082220143976579354392136627715379752529027471716966012381538802459409604897867012386933986500209811720725405141 +5447700282385988297313347229401494009078606483625633850809103068410968238448493407439403040224936061985927544263475630989480459 +1276956495071860259959641422731621493130581830343261790000300846232499293476144299954615667025912420799180581171607386310558493 +5072567626180671481702538305209899197776259094898315797269647260887831166441294066368414302707370453117631015362578331461379694 +1152681127657219064376008724117880126055071670561685874051473152694654802783634954044854697360607744867225915714681210070391625 +4963921877303857727536312369203728666152509364270556408792999159107647346567121427484742607873371889230959069964077737186080579 +2539117036539210376394880318723799879677288248068497337514544989379323165067241670457629058952981263712005132132192204500173772 +6813760519782616773952896216661044250831393786872842763541388164566337619519250295645333112400558661044316307967715017816016933 +2537593940009216399181185307950727066601997003943463162691283637037387944868769661465346762713541328987499170590124382561225808 +2775885197315288545943935069058879566010060299863101752754477420864624440405321494564586899592105748495227288314078895511070581 +5073590254176183749859074321316849201765757369989203202482670238574028719679448337609628523768999643298161243528454637871645997 +4654524903507892912886995134469509913539767744116890388991007614902224668150391846827264198768560243067636303891131378158227338 +0324734826151939630626306117202466456571054839140771329647402994885021118469683547773949974151585727386291916309232652285623776 +1825578079957905035730446040937650014976367810409736691475746813209385827351129150863065044732028040265416217520597270250595073 +3855654215693196726130528392601924163490164258356468808028166928343554047073423362885338439044483545871577569335583661986547054 +7793570955613127244841652451443245937038624291178210631123294830620894480548406463322651814043971289378075631025886964612359732 +2535751039273171851827163526482017707846468207349048105786558968816006070754676314714316763865097547820424829616783998998577881 +2844480806501005182020568712353744447818200854387376634450572497810731969576672980314852204195217762355809650187750497828976590 +0779679209849300532787708183193857610170295918273464149221680774855800423860839661690361920242424552243626034568783063475060030 +0715723560396890811677256708000602036818903166537471742271498254161510038321801193648871563911124555662395779532361864351019488 +0796637275901473841659277764647010680246421160415288649143217531307967149087210418850695712273237810379507829287822942082290828 +2503374558287484037225872566037347531382781922322502069198289211129714070730191986360007963273941619249637438751324038507743444 +1257552500220289953409007738059857241268432142060119245938800646680659562080913958426385782354999183544917531229451796428251407 +0627725746927523912542272514587614535547975158991752064909670083277315643027595358743280831654266470167938798514468394097718636 +8318980485800264189777412075113224818829974167411737699158537404003402647667510002613635249834073904159437111364326870286724933 +8736390761693005617348059895960699070609999931133577397987040270060440645814471436005094800905267014783073740415604380803498206 +5542024002024944354928529036523594800851472969896531771028010643441490945234844042008320398276632902810470392426566175025658836 +7031032484519885828134561441713514585691824451116624144085217816423547864679125010423917860268671111391465300826171047260397179 +8220783551779338773320989765886440391559510492663544076020535514257589749352990988564034320388602959557187208585104844145026439 +8020438087951116022977373383495534674635129594766601601387483705748972280627013851396530590121458524210171753315286548207527820 +8153810125605918943918826453140421737748512940456044065444292520385926730447939082216173702077609971171912049413332448203544586 +0886669881112435179401237673978622874656264303143999122578352317897806033193993865036799045024203333380525337357889563568831448 +3748676570310585965679075167577833926365276411040601559021494474638574451420952356408036362315997810486031449429712897242856220 +0680179178809075888645900355703866665848623566100723748395628721560414660071214664781095082893581316152736759533950009932003975 +0830073511229116156443164078334884149650348116571315260656550335371524295132896582152314795929439939447827419494358066689706173 +3038785789686019127115381229380791436051546935860047229508729818282744657378118473627065099573727853891404083449592265655152693 +7387679714798835215773140865910355708661822160467627107526217765214393654304740207983760504979185513943991488603347106187504836 +4959345618457216694880885929572868404119594959297665383045694122211724921300914575522725438253647266552305439505601459584619177 +0467037747326795908961510775114924268777468775918327644966642534544042676564308146590029193764372898938024544197186375500357840 +8285890907673262212647988009259010379723054727978121814340184295658072413264615156697842474728575585169324914900743103535246719 +8911662589073033021628854642652750279152366994152376376433694906661951714454207190114956328995857009825646477784015810868978338 +6105850627169563438884040914539421660727931317710596144077279839188308966168911142486835009941100149861369756674171355191560490 +2993431983320455272922883788672722733898158280538086533775288120030297726075605253307507669239723828996651212708874872223213527 +2383871203918607138972250186349539349163142546547549565946027359258489597478748862408648058675393278207850385093331126182621573 +9149363763947487276724850656041160743708661373179229186384365572255499194358074217364047819850970764281787263423869876082053909 +5827019270739581666492975342346328261479975112685223225619643483040396563898673337995672909483700478627244101352870984979237732 +6998285824391825859392150613924432428270654497491969804735086552708299672203935039153979762171440238901122660133544046138761976 +9253330844439113156612382643124565174653829532487393981769888635312756386927331059248556801164857640315799609889660186200071275 +5612390497528583434894505132668347409216226770741805847223173711771542665456899372500390460954330204857624315915587829701045748 +3597132578062559495102590042788395347495253649400865738509995118091093608521019322954235222572297567318636643867386469580449507 +8425515407736939284693677162137557410259923224830338231554842919234034510500493129925331580967001474100531060588153614511993338 +9601991297057389436503665001505251866564553593237174176487901010113004032749058341666775216970685449376017846133214652240838962 +1863773381844384208184694104858797647539515137932214592371546918752008772143433088976967184397882651789039128762093118018510138 +8436667156247339295567342218063358078249911156583296510351786615409192064411950727914656163850098232887122260394676405120111886 +7683313388786537579802951656666063632278774842670516448032010207156251926218292168775800210222391153956472045994503212219850049 +8209641417161775663979384085179876713787424866821458112247475298894421880117371699371735285979992519666751909878379569083863705 +5145614759445599831570000861590726100099887859826097034388914355367729544959518374514940678332048394297378845981970826760413852 +8761767120217732947982706113384060618324856798940745761981931692821487049372786616858603289668185012412091827806837321112104047 +9255297129664160944098264261547065105677964568103165093339743646349513755899758152316922927563473869566225326724560456097296052 +8120768832977744822631836021474402079390526097477803448973026451821196612519143995594879311933463926655409414749552006489267940 +7017154772060236322937445448043347707331947096778317016923510498198624836901896034874919545286197554142622789268469355512048260 +4180409321511968885143370514200618650235742405287713769661977834572569077057120974054874981770237845015640851329981775003008191 +2674007147813616401003929214576628097932821870524475792725335810151140502429235053500438517449175945097953478389066531072308154 +2478433215760250770512406042154394331540911914042172939884164584979797404189940441889258451913067147217070836370940898306980314 +1416691061574950251933857581530784660502363969157027453652188159283670553356947339926944472421121743170291855577403109297001923 +9846364489252978228078702013876774947354134510165101268064021721322665802683013959144338916544348159574015107727356473797086745 +5487580365242793847184389294498037980764980441864399341257536426029899353090372478661527651346349015000812904186890490170512422 +0317001187750772629199900746610544979042670580491329322209673449299816557793150957519344382208574308527216206006597313873698350 +3641287214039996737900834675182665853779449596351604582635503134884649233644728626688852457677654231608768591360986911023898427 +6414868015450446904060895609231719309486232701910101120865147273052264604064433289410480856555673255517305129013544577242362891 +0524147637426415811762515415524550268148125432394280267631328443360400295790051752698236429135771292548014257090019134991791562 +9227410989021340348714125220582535723860559138846179511512191891037914244234245126229571603923532773311387103298341477228375501 +4041581080402587294355936437638145699274625343476210354812865278292813236740766242635378473283614144250925302804707242340663339 +0724867859827673965725683173043844289022888248735884487800477685080844896245015920731492673699852061747889861151283779392092379 +5909276888786182094219448158615991460321912732697015860009933234510620643391119454097956755043612428806923128306819132701862789 +5863973408297121204616907602368353757254965361693796331036727921299882051062544545347637276552415740930878434572627384251645258 +2609867181490019825132280383807303218611438271972259358234476006167125808789720811193408861105326321587061359193074883011503250 +2053550365140441314774770422273440770268402358845803831349391590570354084402287239858709867448417600167758148787965827714400727 +7156914133575985423380170082307013620095209686338890246264669241382877333719091759478776099336392938215841439510174135934515390 +4831194527648288858829733221197151800308714018772578642206404911045931696147282373784233544435117870290316107286565703640910861 +3300965303400428799995252612196866095687126018105212822125411581947708785019993474053195560005697252054488325120896486904066095 +5685043938431323336868482886553258817526871549549389321696880672458619872963868160503420041705844727261813948644337640127844475 +8437858633093280150489850618743229089133908480393577494682517449743840953324665700848231841040716763791338769325607594504384201 +5092632498177396936492730922026153200701191354311511401621705191001018224605697557158822145660532720002740219521243300566753090 +0884695364629948365817606370132571392316227842180869160712531089788912972870842630687441550199592940590099216041036498458331435 +7030125885795773170445439886698551870214797580800884149092936799091801845209138388276070168782087225547258102113578759536143915 +8807215865608701057987377248776702944861046260230342503936276411275965656745488818885694005475308409675094846381271540973613956 +6869008153507650455021791206721217199858946589909935757514740552973399919912521587008600090842850454930346920106313925959869436 +8747302342382237757458390343882591560184411401946674013753631245032512422678856746902092711874356365404625893572768808561925896 +1816616401614372016489584185015076832989812362352963164309054653174766477426767816672229374164734973950570074316503361323429424 +1677262729710288413972124464110274430459973064690910792449284824422312855723898793069294265613363135307936039462077553033385323 +0904814204574628728334178723557725337676246503641550973591704771295724843533583760990530372617065538876854581885105724368370399 +5318034405787225786821497050581659615205871549939335953839157390829611482749151979571742480565357809673956661177238715716708421 +5050974915162203984880371984109598616980412684284404767484126024659745317958728060034806278671739980663056141392010690752871879 +3910704359349884254016812391071406772666793577154924194799659346664635964157482006918898999240501149700799097400684650477119203 +0922138102426301243886965897610590765176893150415296968278489084200960754236095696787157337789651225430707548067049568646321905 +9034532106902339573322445743023271918597056331210862099117907113669633072759130199009133076195985791779283506809079562559701881 +8961339360352958868430789580169262538709387737102199957461921100064868143051486287683576990974108237901843716454745741895109283 +3895519300319620380905559168583475674843131617139726705719237185238119833221024548683898215318859806049104445070815116282206901 +3205107673082159654286800880087962054713784047147807335322961266542952563116519811400145837980005316237797804651103815235446781 +4645211086556795731679627686638624553276782440503123281227932461374131645478394881186841410205689089468850242938656791425398127 +8527652116361873059872046061450470630778597394637649835734537239971727675715042761592888819669786191505268650105293343814467234 +4729851960520632361339165114787166123924351946142914574237488059076517558856540707604936767815825051810582920267228276662907034 +4922524498676764577221603274496474839043188887553906964245432382389882944444308052516105844797654740378920402689234115734188908 +5209597697295900954976185802188459574638626316316754990028635919463918798002460967253179124238772322245122243876066379489508079 +4508289933039159505453489164861720999217265104842659882479905333119333431390108947303039851217725991104705191344280222412086293 +8069168568134508776661939124641765187854999589971548041638843098203486147656735371454412494419404765242018465434846607552127178 +2927086453284193065980223266739988186690361081906781869605569767794118257589865157784413584255650425147523732344110338440580815 +1748049647890769557692421227727549481557775151410048868814718985045151061237011017099100528551045245586497019590105694556854099 +5925541326979395577276086387515399363166724218914221343444459910602377753889752768375789640169881596578910501727353216027189265 +0670300499827706283025120061244353545380543254798985391943205942681540582832008582661334249048815794102101737939011541318568554 +1807401946172440506670841188965007674553832019157741404977051650612570825055419487071388288683499429125090172081236081328128795 +1574208400432062514192736694804153587958619698259830513687209529110862276181211516095950235354568516040903749851761677661305637 +4121149896620030430814848794433445919918770329319490730619096312332656236065824412598168467443676868836685315118978623516376785 +7498488621509709129976594981432727530944618723741457356588286547102533914121132990321805599546945869002131411100756554367181741 +2582026542886480869734858959495104201677107827620049653093381222272376273695750172303884972292405628020932588645381010082785621 +4289447606077369793749546092089501817143072386741950941157247080345113014329756464038358626070033871947884567651488044623905631 +2542825564877135509885226287724296817068865459213301536696178082552402072123707396192496897302967815948042943248544343979991813 +2226470997115317096784601264496380718265218871198752715837658374418501244345114451866417686910079224568758319235433015428196960 +8662720543840647608329817530425275416788291903848716280076563958924728751343578053506896388100895823567897044421928057610378475 +4719351914296817015503837199703117466548446025449281302558021903566930798786374684451211437854302542934198900108681526080958767 +3447640954611982803365820977016952430036387460526610055495899354375691830575753318086903113772624764685535267168082007345740305 +1797213012073375178436558221355605782112077809669165249433528557235690262339894762165301931035587761808175461223039297448962130 +6950060736132243209446984212914140861957832417030579738364918001660026811196800334571314378298274260059705895036596817108824926 +9243829747951735931284002210476722019445511064984030772651417194277223668272106134988724834976681779756839767862454568373306618 +4177035873387789691442645602142232402697759139202124087044601161733861189197322918438352811683833924407803890384828993570282474 +4594126055178872656922618774167034354795564094725497124745719061896909861368756961278327080697512762579612758334700763137442983 +5295668887669717485226390123467973887358507953479829153694623729169375830523050788966597377361980793027344718171019365784257200 +9888285370457729318303943315282858750583399490083047557333008218529891417381026015175475841289968239416942724745429537867737999 +3215943066766119309004524839024701086312759322446215895076113158100056596170634794503064752363481133827922180405978563689758589 +5959944414411467463305736849422386192090664596675188756343645002538240622043092367067855848705606619349233737685916244645398642 +3415921462522850660711019463447810831161516098186857649733922254277645246597288489872824421913456035668900907466154510857353878 +0820770271351282269897729773480908180216209552917101530859717823023591831191252679348064887785178954716044181664367902784712050 +4837078657362119194233933526772924288995857318513107274934487922952892071107573280347076419494020490239809987105400407903583211 +4566520950673320089105623215464822027247924240083971798563728505499902175676964769999078225165976694000667473401833118202304025 +6860738161875565205941822290237567447513259073024464364524976929966835518792473395436546988594137222229184152759954076791794830 +3322947265984158060992158624663708425488918876383348951122905125836522038089874259881383269124957101134232722838965307585611944 +2514815461210894795746476799700740431234580399243562016925036790171527515771508119826976806830254109945111480181093741053447775 +5733504011133442490826886677777865571712956442700084777358932735387221216646199198889278425501261656719619101527490023033629983 +6816820665025994462583833453804933866931009904653183983658207938144775757074429801900721418490709180940459786270732207357575723 +8952696642268346283355069787962809893365964014926493914452473794524489554221368619837559042723108457610361497745820672215991860 +4686726618791630599619015305428791046600757619220375372642195506961581791667069381025833026424470965152344317512976704970780861 +7817535100493702359961847958187008692676255354707895369219620793484182638707169828174557995935583658475438688352992259510602626 +7874029548817067666626239631331985390475404826024086339339243583071276348104332609325698630718922399423989995185805657290031135 +2250809137006081215712604683009748641626154723738138936917095298080941105465629041740614734952469019573555032467949707456686827 +9015780860040863502309304151201467772727168204445161990634325579174702838004535428730659838209979201561616665321137059277802195 +7650185512550554196643188069669262493680280748343975919639135449289335914799374871448334387269516977974949025899212288538454112 +8893662379603774306702720358700430713868475310661351513479439829552220384722225613968805831874918699875259504799262267715048953 +6970047041565543406988313458814942522252185061892588920654751003945616453031191382091583403673677641380943374412110595010134653 +9871101512999920825593895837672777862350486799080847951285270490204712290524251977042420133385216867854292673426124017430550114 +4761750275074356557669956909154179090706612051478635506091941757942400068895124161242203046266249256221284982009220935209300242 +8708015446206142670623126732884105024418284851295914904738375122444129503796268180002105711502782904456582492253779456800326918 +0613210293625239057781585677639979950255289284707390038337154693725678298193172844000279478330485593483147704265272812526895779 +2716164189514060813320272610067774089300292554132648786501937337701166054949053895064937132503546260829302791728809439780621266 +7313188607076954454240146509025347232689443993903699487707438769611328865209183398377855897661913914996316621615825549781446347 +1784361684093575880030474001414660619024654155783784777347697077774197795864389624206377564266630250281594010870492871026068899 +1515638387019036333906018359538519902111800466842577317066502746814101599618916383605825219038858550204877062732542814333739145 +9220084992150752811004335287492444508585899566926596972214201784424729857471199982968054884285888711269768545472118962801716609 +3225729665605184922610258057387753797602084282592399424014307116412082926636660405157448703031528431483170813038850013412917589 +0896837209572767267939653527113544043129466184693048843785638628896239995824306624383959725609177449846462355536652421863726297 +6350549459745687058464464835566280558244152696093628407065101518502946110700562358545869437041517797874386126356678480069546762 +7437438880206605630925397821472561939196877744334362718590301519068712375301433711985727209285202108430567240861306104506294438 +3997013218626793371442945794751768102167128014691257491799130532817890787884088430906840628465718832537193051422942089432586522 +5767261809159521411906210527732505137517003452232973525892796163942363843586179566469412536807588910846358095073973053136806664 +3610420406657475104037145580225980872688624964111041381549594329404718245302720059446156211318636072750134363504069278172819616 +6203445793416289171078116228949526537345367573758934511473904862922585007326728074024526181186924979509692268351022430367531902 +6396903617968213357545062076755915835538061764765669442173658896006627193712410945197598872950553456423579139305216599382954077 +0380250815827704962815549731957552434336266476872009617222288199268494650352751383734703683895685962764217150385921949138345511 +8687569627504118278687806727964146837523838460065586633962532325989849682060922414609471208083225295893531164710651416235932804 +7419608622045901350837303269139752477969857182975189849619797931283834755645259685017775129366698872709262322484867769237487261 +2446574648357299934768068561355356119075191099390351323266829442188920299959413380468532819783426950868690325166385302101461953 +4690378580819708064900475904899019106498140395743178471011674929320871266333196561704778715716963538368801024314408279647276339 +2089232113041890885410086998151297290297216269518414003124742833718798363559663862672468722436254498875698485622178498338429914 +6550115139019878137719854465346267455296956376622672210869101826618943385242099068657285404811000189142469975735597865828978322 +5550336328979484745931555695487878550858216320770924583332798110114879836414978841309407295358874518788123614542792919395667790 +0067101966073227418786203809533671075851186310058420930636457857938955159488144881158165188969895896949949174391923269110027161 +8258211496051765933329722240348370716824091139227581167499651455166693356343053246387421021603939871942981451611393932490380081 +1143232998448167618984488431281685110452032295577636699995232387576558530063365443650214541186195345336376561664260667081107636 +9153874053518331378425571758003432585952239206770799268110257888073536748287123822972824857968518689632999374391956653833541643 +5746454771428065810576182766708428737462219904665952012954717596322305804801076045993811560187169208752861306869132853087960769 +9312948167988600676884786283729313554222793557251794907465964089218336400060941052967060271657136677975942167674352571570304004 +6999898093303477247632176216594681728525645905467929567402604064471637964034911185817952376210008390273053006469300379255866531 +1000632388106711156678341302292429215991586500122697419661097904387224375829260375948304920442750157857268831027577641937349960 +0561600453741069995026478401094735925203677529242298315347820295756013844421038599646676315356655360880496025651231310731701615 +2967967299264819933654047107088552569694325550743639578846283279457367587777819171468331383302126179626852498959303153806267304 +0993137404276474214149260182488025202243237418254946278036864473980040065830274036040054866953015031416924556900973301250401325 +0611401987313671825312920644599670850377089684826838922442681491359233283306670402386279700142617583474055662080523345059224691 +5939710996631567268204735821745742750433473750474145824948890280584535509141301239485088599010310179272128880118496394992466934 +3357460688499765569727929892732936606129832020628130664357730146150594561279515636082352905831482949527411745350155774277195108 +7138561333287682465057643618887421608272303910896561253448290571853039520359195699641420904384787139962581486218527455895010235 +5098065276517497532201660511210544476729427289544804994012760004734407796257346283172974549503877915741488118551582294732680387 +9918245303994525211620190522775746453648980603544567886385199869862202445674868474533709002221515555801308081350907650459178667 +8721390287975913893819238889792260446828081274233551710328471981963516819510944667867602212793558775770710289206007250029862895 +1280952122455041043530594319244152931145497436315530258653194357643115496210565410757613267156777417895715618326341142914815577 +7155758483619829149138641241642134167637824757268284393191488835351550697663275036059641871170325362834558782380774852273848821 +9812670602301209189800121141901793479005817704022886493550881027819499284489381378125534965938777548562514868791510874667018247 +0362874203793328000664031717659796956361927785166994482222033051420000873310315385500469809294516134684467763731164514267312822 +5076794324739899424289073581881385172449093717540058394689713271226423655501988369216296397971568827556688651950460247532081671 +2211691279142831153117347297369818334578478434502829176759746633980926915557525626263924160377780019382684610945724739361356924 +9219339881449560885105533284707871200679261887421531149034514599874409542178946360778835736536874753087132328398630153344208176 +1738323078966650030040964282424869456011617229198020575179773319844636313591780039750411848469791355249483448877218482996481058 +7662725242856974627520011178425574682734942204257582618062074645522175607162830607624141450906516065750218377963070171888324843 +4118042284044277227029777215730052173836438075783336538165120621022353806541561734384066342123951846452650796265263415816226504 +9577454228253953925531431429190317095770965424733462598360193026659796995068726322946365945449373804244913417484873047389958047 +2530447003714965976379236094037411278014717964765934254550861704945379408174897820528178412885512242072953354276409893858269606 +8161920683332117194434729439905850512943519462231619259091383414069915670154194411541846032268055950920748136328515080957772524 +4455001394112823835403683958904433147118332238523201500644925421808301905477707785391327088921953707802405649821266420465223561 +6446522347006213132203935166470869874136295692811490433897687621265910913874552752126752166818595258011990560857727236113871905 +9408818174849694926607806886600067803753381428797382723099805698620415041534972953179055866017711870699147517868454045402246335 +4135626065428509052074741682443536368971147858952760160985560880547769122981012756994845114634562103177568847660082033374741355 +6629483302659977998713743227246471116209038004192175569114510152669193408163108052671223081581651955384894038833735367319970945 +2019914369819713476232512996482094786229436696697562766992595975532622307992117773104105916821954959122045124593739046172932273 +1444770368011659028126820519729159232409884803442212877208399135578736969682832974964729860145229172744181374244786183467104294 +5285602663449673777986234552062489192832103368544537902433053581936823853505740285888319749907423551259460167786803225430704894 +0644806832972865011089771228494426041526498427256197014809301589227000243312341210098122668991020924989347084902763173739593342 +8397927825150753855181618641584202156450292633924809550510911217351590362959737037094558684222129548773410325105794129798697578 +2130742130526719507540424131716191473755652175799405630787752600339151530661377691130274509280084673031861647779131553466473489 +8949445645797314751748458206320095870152672752275260151260265707878555413048057771903238190200758654160960860864191150393477626 +1150155207111417862229200794999414320488018094694431946879621916439109486610009948685634866137188097525670175207351289611083094 +3459879841467541763732336521769192414341573430800023957612803685733924880739274791648569413015334203212412056584388583105695911 +4207107909576145869607620085461426361544959409669769028641359260476393776996951928885197026584290166920423018039379739461412804 +2471437021597594937922441016847924078362096363319644062767483944790539470640965768192086646725793415577156194561524796709562342 +2281835051710898850388364318434644955789879751829726732802303436684019874264646806723409567549761608858039525219541663231620839 +2007932711287625313296243751903200023005400873291491556838807519342296983784742524733445763971687078693207166472588686547509634 +7628738900800911617227705711930133205924637233313615650647097515124961199263711780307927527262674727498690057241315682922136505 +0493387404859274797132212785618181333173806092520644500400240017059008198178246301124430115868753110589721037771703980382573162 +1169775643061410030855660308847307357939679557417322697395889694976054211267593984893969933792226845025198715121269754643254572 +1728088919686607770305974714811474045083911324960307426887087611368014116902540202262357011208475439646884185432232498389602471 +9413509858059972641349538923181108900150612516616095667778909942463828217847216087159443807284569471198894585206492715160193699 +6728843665008266563029199158801993529576549886997200057517865461162768723429256920925422142043184781462681238742660537092344889 +1931599731390297037090231505612731169969829636622229470099002019655957459951531223643763907113518395449915860761987492535032339 +5034107674301251502545019517381773020837552557746624579624919732008873147762835295630900229992866373514578020722517390727520774 +8983019600230774694685639778625463724553591351398381775880431832781553934906986888770789153951456721760712116772577734503027958 +0722579888130638672105073329597090462417149082738030373661949401575813793082783579270436569787902655097641040401368636813371427 +7854245354370422693429496181630052949425191073877308843275368739572310979470712820505147110988781661197587183674125970895995542 +9743873036934859621564820909920160132335721185173809848423866981847229831790016569684947679721801847602901060080188329396305906 +3397337816832712119749817143233767742905059889912076656163289731022283191890667300707504214144077245819520343407565716275839778 +1635431146179533618869624816029047896887367730042516939278216433734900869272992829571445669607086349418935403567352277456520703 +6762725385281999872315155129365300727718531348063401770341896591345777984146395511533572557918080490707700609701643325567678181 +4555558906784569913680220888302706502231120206554070127879841389783669163314482027219519085496782129580409038602612227824819543 +4625553964652973015688630098326028308371988997070002772661734916972323600534194087341555003249709049635157603584665553841867805 +5469615145572204179649517877754736243473051237336198742915870594247954290354530866927776592105619674425818013502199456960100742 +0688295929947456127268347042202884584629259939771429635850789571181349570944477662818026788804604769581716692572546910654441173 +1781053017263760670442837500622338443773255350157040419311002126378320391255428265011649731680948453211623587016944241093429272 +4424881884807343597741531983608658269276205760340198024603987236049280398955549067330598233254907076437393855435114528768763737 +6402747544261459973943024136936806092529416389402993987239813694282531255632557296852083229095761340348625572700195393957513287 +1528386320984169104784676953231416483588713316281109988780398093348714641247346320098277064757682809088330401558069087928359793 +4836620934876383780592087107373057072272745160875663949786761387509864704698529847777036861011698566433232612309834845364039380 +3575337583851040701388845815054721473930456788213430385395140774983959957647809720764167375524924858966015899435919948180069180 +3727628368963430480869218342563326331182798677393171005831460902561217359479597768311004931085102290184621530025879576836660947 +3475800984573188366693910563609274312878602294132115102801727644998770488363543862638723091471157432089891694987852322137790631 +5023683657353497036212732071088147337120821028934424904568945720202766911488162370473709710449329167955152043455364639983165681 +4313458074953427422966761496213154547288857367926863109897916806997328848306557928981283734566043834603735209469550380544603011 +4769534552591442528339142072463485535584209788041390319662187991947284630203143273451030277553655546597381665676996289780792291 +8029659403865541723176070298858179302484672898090770785814054293418165410123040143090326214914018331298053460618863938939353357 +2504331261837289138046426704519324433487258219679900506483638261532541113835978749947580235779347318410118894160450785891787503 +9154332931976583483472163920590162126815140207546760699499178902132160947370072538150136322051659957346811166868803166980109050 +0222142484013400331297859576897114613785020913334195683798252363342695111135735081039945217905782047137997143182303582893239120 +9813120425348259494442258981684713118897639132890990099359593568005985530954284388860261356040562648449500126432442951864251191 +9491055336614888264152467342246996843863880300943314768116172425467094897669780084992632583894436406804533915530813948074710378 +5125880262000217074108251922916601396846452955978383367381037097928649700928330600429463193168987527289334840125068384753709726 +3250963913544985283516074025079436289530032965000091704733601456641384410110473920750483277883697522865698072624602352586775278 +6909533273650494192857472271669762600062165210442469141500817398520087420757013742072500281461753556164642072928308813935348280 +4807129228896091173010215184046472764333315290810434138260279373142369497015166184160402673619706065463269358547161491925056454 +7992119117554165493880872181423830045252323577778753354174393490633929436643377607848377532573742197373252323683113537849603683 +8841806293434459100805456108210596607134914383853585818153512788887296784056237782465358856155449499271949927041469376892328564 +1632152697497520229603928059316997370226452543375632335002549957119486189260078571706186257203470953055703949967851861921520731 +5864396596718182058160283419308429964136934231008821382389697128204242562370820069167753381761875728312424003987892871207159646 +3666084774918882500462621483992172725824109862896378199142479129987559328773744933469602552905906375001745899273316469658254151 +3498338535962144862096688449703329334091149790999664223656029226119319000718124150843673811902580542556051470065598276315050434 +2588642538822153267060185463367170199110995449008021906678609426788891572594252587471982661988824671301251499893937578926745391 +9319707795744627065170409664872689583830132047201026242383522246999299133923376128562725383185342894837098008031828134646969110 +0671702811161136021518386108886822361915633677386996222331112492876474384896788653692739997207795179082317779078270838374606210 +0529680562308620614160670757652951824510508056181488035417621544956335930822186390269360882386498654576366806627786546389748223 +7121344007162987446407144751532917923911603613369296361423444081791345960568825786153422255467670330488573438324604236300940295 +7856126749900534423962976560155869500794418586760356971148877729423323229897743690930063998855496652306786264023420507384957613 +1115472217398806310578221635364703110916537667405172225846214402717552527160481181622222803507823937807179294058810167262993525 +7733396618045594337624445678237438829647882527257597412143681872681853625778555809127382419530057648275810262641644934924071181 +3613929384294435871056622853422519700112176582857461049491276578382237331260522744886532614099866550813707901946900039411643678 +2471863854966531804604197010815017888282961247506056635263741803813648407515388558830396273074055989597305977778886391310447068 +5772682018682680181055188634541777077934406094474845232952841473576584969574909042981622739932442934509311768673906919851347223 +1971820335809415226886329944736554280331033759806916931583970783266805147633904060778157532814909009520404718412464333929846412 +9304789467519446866413824868897777103238294214707967726874145548954743749977770022421296628566823077488711722686581183789601459 +4520829387352906480220193366391017100275344244933837568936172706668016811057327408759076674979685576296077953383462420493700931 +8246906431343919288012465266462141639350962646993227082304804964566869105156074039984736745415005176673660764795387467919402546 +0186162998111273052854513690953012281012706484313894676418003816742150384731546652072390520748548343581415404883693106949598503 +7407453409468893649012012693295080963235028162714227398127099455397823529445343521263605978197734068303737481555026299674237511 +1229395187707069269668822522188040767339600015117257223444650130799671208076697307072988709360546665590067811729152682754853792 +2543283984004058807502208094435749036903952553879602762556029245920852075326946498509084553554503835502118795067551626235164267 +2007797747581357604343336945727914338356814330722751353631792234077965818274628997502349441503414970786616526353391356822676384 +5194656346701670632084824377074289402109258239302810982753490912710811175011533673266509733689435108026608580234617657393376969 +9037146495591802460947583581948869619219502922722697707400434384802310554421740985009894719409552319891457897233615461251631720 +3990697080273078766162376909117528749470352764935216917028329174981312070290561402015804824976866738329336998564205666524939715 +7848352778978178394189920640395834604512765967920164240559275230242580756053219581598506048812354481467677378211973577424576951 +9532477816991813127692436491685156204938010748592475096875883131377908630365761647389532075843048673325944084338736570456096797 +6164242821944626959915759190590818947629953857895701779138738262444662644883838599728736964664029753263536337011632748825080378 +2817015241236739478928849304183816788634913242150862112550226668153792859950263630837585979285928908928017849179569989536100198 +3188299380080735588065637018955883708261980780287989654193640067585824757814983887808561472404025858752559638368589207857289458 +2148159553153805396206627740954877447246881212999958661604167091735503989349952300666940107138834649908131455364478740204253214 +5183812867943299697018327767670320460762394440257519747758147645570356514775758990004394658301628734229570022912960712413921130 +2264795742893091605616321707447640168594382931899898384410937459110317820561426704161424031904340033048159158399357351652777596 +2139572365125527551258839222311219599103367463992672950511023648604600231272790630354961828455031402437496547839035832950304233 +5563173043827945218120288721053385228256759107947287607765533263965688120561373605092351078219345874822210656978425318969319770 +8518350618323036588967937820117667974812517069373788130975907844737917546000454481446588184420492343516807727330574227463796535 +2208092079415381688598883619322045535433174373692220944523386140424053570720154062384759984606718110454232154813303857905183459 +0403764445312406400743177177292964855962834587085077838226697948324657642954983884229554291774482754430895940798928630071520949 +1218078030268093244894387180566099112644808636942463725970271185753041625812549630461585448271241111674252623627152709755765524 +4640249030477405125378434884747349008061139769305596841781606951188241867017869136729477063722445205619856063985162910427240398 +2909590245662898324814954125183873527735419113049232565629860534526664160220755933343864533662307471463902405211409478015729764 +1532024154051612895466466141175266647835257739963433595273778351297990606120134172421391748505385924656782899507982823237116066 +7625676430217706141880242765715903813773188383257526213002250997251341961521914541749167347954676286972265765550795698115431540 +3971204449523415473814450392924406717851351635913252062373246080376997499866289003353070967905385783507116089156113219681812229 +7080951492315882179871809720207741914174481310077398153033680591523295504167580131795931815413662606081970038126846874489797367 +2964040579217611595547760234190477290346436427835968877980338437548172752209833653855504469466390943293257324156532489625950388 +3972616808815375498057586682316341344883202169607183819718037286789995473261003288017415010063837288806976576286819527639032681 +1290138051725002391039102512231630818525813814320415691425261417707087230623051869853213472687397101016144678883680930245168423 +3850834662586016207855419987088023334696460550057253060411826174208076438814838201928582833181083830814307068201908029863175971 +7944749902703521228274612063687297856087416799604592533936372400167341391775276233906976252493497295192482610468181988345091916 +7011096175582468584236690812621406307216715768647084636075068364924266221366474623044001441060540006895424513815585241978347109 +9612362696533734373153334851655466254197093486211639205861389987966709302035551682330490724447416578042696870553662285651851441 +0111552534765233442760444257765948564305822466469203165903339169326584537556214779726091179725355707166736526121645211226756106 +5885039259881836616302187141071464709494663689251644989212209295897461744225634984400183422383926438027326803031066696585257111 +5396579069429715251643225942479087285188610492026082322649552644481241838275988175206718743351678720472794668564948833054357449 +8688233606386980591001497882755114729508367160997540301693192893291539036951478433163921320714186025327605130043325851253234451 +4872214673472278544834646890891722960548978190052377730643946484747055518801507106492936171927846554906068388153908514485343068 +7214840374326612339276352609419398397127314205763710059777054421447567554750695002090543962570373028396055745121721373269382936 +4524482436241914482911352925071095921316760701089039169544052169552093783128384931032814298463081577630793680822839743941613795 +4223734008339327317027315036935919017365387705335808468598641390301926191406305147914145568155445735877807426352454856646393111 +5101170944379204574558835956241764204898906557725918071785044288986731351021322301083120247570462572136590876553271594974477165 +1498954940338463049076496039744592188429182308195708965464178219513192666612550201124332096109168498851228361187923717360547190 +2838593784210615507907908324852279814008797652951109874718831612968151452611605913542110950712021617053098075818112497307624799 +9381906333865113269293243176861228537211612376105763917319466803899016867981408631123044887142147183129506705177986309824780722 +5409043531218283220619747469806558139995337403975277796517573718341286548125289716392847076083882092014379916668361163684338425 +1904893321014375227674131798347455361695847285890504332145250525132622615658888584205703776156043946440538442055020240373771823 +1351830991497076875181168528408533516757044944191964801761366476485666222599596030732870772285002472325833568217236712614883027 +7315021210616605619597435420913698876375528968662848736161937624720303296895982028335232063555260929924269983942028091369465040 +5072200871530059327653441988927547294080741487165355628851444397293608964873155978474929308924899776875776153220999350338874781 +7297806144047991652107617589673777407092234699017295323732250434070279346790076112443512292202647551871470787944370705490593514 +8338680194506251068070541345811382066184901866943035469419313397753521282182668152084983612781310002659403231835343007351253055 +6114581775690037701739519471149107309520083450078485755172388779153808947468636573364766942355736298359796132744647242525151199 +7574059840087355237287159427555431111919013708861023222908038566164641505519732443177083128309239177653941203870945389834998157 +0673474392377001399825892148070125779937209491690893896089896421739180565135864053866800087116893052683453216209088258021309774 +4693630023596081716394525302256706650097913048822663453433130852833930985337805085115332988734054335391426675730296735748012109 +1093024463171151726057935490277564245957695353097684106836395483499057575855912524860400186656839768087931489404973303617192884 +7635877951810419014091014411375539957398983529537012020519286943703105851379947162885419241335326379003488660213681674994537920 +7313912009934610962402221459721660669217906503605255214027757570091733853488694769091622366303142874602414314687218525998341547 +1566050559171006939210753283371737704974135991991141945660050193578931524036467433476181347487862613613591250793557188528520720 +8176429431061693107924500762483197651311460029641913001176379201100638867103309766025948024030323914130084770751860668764418979 +1729648696441840023063291425166270194908270401060650039064514332458826292034060316832564595611388754933869606578691497545240684 +5096948893204927421293649874213362565576773971352082850459718459918977951318599802521678656926223913450782713343578245294043312 +3086541714606171194181739777107223773075014101828171109460641650405365226657056099487143258905930679574237317845176027548390939 +7740302926757091096782313708390498469422515280866564744352174072524950077317896546008807496266670386420521998084434771204151893 +5027934064208470666653625072824590692528078583512194513874229573957257382460729248431045517338285948895199087965965424240384379 +4571860754401274665671127939865476298997613160622025616980368067727347277864838846767268485621173877779887690613158095362529635 +6944876714447267999239762443038959101083236381747476636643523496718859843462703424789350147757356429745707937022796547995361159 +1608685279655308172831370710312433300754386599666997983340912162248117716366440489547493562293368632280675661325251959965144367 +0470115155279779686716584895721905617913807392365128663764858517955975388392843450401077964641050043314321327171303982059553970 +6215103349524529478271694130462996515027440018395285605756916821272681672383831564950333676633547276622946473458222960324229716 +5752661076880540890733587009248458903926085507327954333035749203073156883894621881819872469950016600034383209674501094959670531 +1945586770541986321354980385963397070039734408942633942296911586196737659678600361467268366772660352728384921986880900512427348 +0224298460720490332231265716874929497392701347632450314627063512555464017409367105505507132766860559659364230297395631822230539 +1702023132625842749699356356536784362975613947059773629978196487288877402990298821674431326788769352305708774873068640207819256 +0837874454109632180336808361233189153201698197837194960277668995755988025271886650717010166150668262322432532297058508033324759 +3169311626099247379274799980132948823347592107280599635475818048919814215035606030291543579920124429871014029266938736200412702 +1914619565140979302991699155094170839054951976790499833713849411741065459327090513567799966465721776223514774008243762437394273 +0284830262288056137733605741424808121279641702537891966081045603173960192239888264018200262442793489277094386722616393425116590 +1173151387211465492751444105592198529015440250049757464822160661232074007786672709170841572180341360083386765961004042180282066 +2747834197332941363146926817441453515366505571978984487189489275517487274745916049268403158626553738640115381387510687487708488 +1958762131814281375452347210594453320648975262319481726099981862965271971390001963271654018154213822420613244796687794506233768 +5861590138913305751605474142329109238465204802867089486028021810232894377402829167243953763417451369079334074531476305390636648 +6931736509544684644323440543056340368361669695138186889701847677102848670844548402669049251722131793379128120867567738961962646 +6821092152578736586590175476051687813270318648206546383329391485549308897384882743702278026541948728683530021908873051239269012 +3127870261792191598343155115866595959890760429348459173942873213248004394755219777755565345896164898508858847846745938219558572 +4104690546998169355396414252792133064383123628337383872945199098541769741499506855287626343383400914652705990644014540006261236 +8792100596328961741396764802811919634888917999468524631681735602589600555328679349075778614192998476904702613310979642799638155 +6734793674993705010403616483433595873080508573310753327845752574399663353072797746592366100412052447212721125322772808600316385 +0099515723538668132596125963466202152817452490708599517233532239221132293841708379481396652887673374586403485713968074564071453 +0083805755888358130747915433250146084164753782259744459712819051842998158461581619972310157769421457011089633907439164606444507 +7652876608005273562462370933960187647117519619868786042595158425921249718553500241273244226393132978251127763667423654542879946 +4405249132813172389850479368504130884719811118887711374371841091799443512801595585892341132692059703325567321860246290919547167 +6941199045895501309914547487559727954258599982156757774973817213033640726217789962907551298312065690327505399614397188477788050 +5358645453073717607933338901589045055393757684759513577031387645637409945004898936885098068574782470685593557377991753572387018 +4706013177574137609818422036943485331576839280415982210652357962891328244924452137995064340517319656166353370990119146719861836 +6329505987385677903166233247184756000826411332850477352758018273420949926789280244169127178212529891658087198774279941207714390 +1511370436371213645604523617727839098695940567432172783593724006252095799407942743832278211803447800402891197805298593983426545 +3022464264863164058488714249339098231034997516428008392896433897781383885524218363385806736382055438093644340647396074293733782 +4501101178543767329620885826562169899606635544112652117620964836027885674519772391619599940633712271697963657935450955634525900 +6375010018855545330452050093482150077359764898798182405456683026765532905675606074372556178229924063905296358938093169081598198 +8483257277634207523851089755598501951207216079577367620523344599612671928857960638762403403726285770712060711385777082614045533 +8256451384331378443868986535492407706512110875647453641525322621598498933275339800741719049869908400644823204415096675208523961 +9398321546440720870569872262936542811668490033035472870984698676634686438176528523891161540254323161470512628073841436944062388 +7797071423711013392162710663609589623668119033549706301015274145920933018984092472146696838533855941884096071158160204252589379 +4261414209517939560365542448209082777049757262017603674203052224119028323101718025778556206926756191607192650438859403548542695 +1466289085237622991161102650836935979121797669268051582725264475273671975785294342068745493084363600896894177743687712147368123 +9181597431483593985916614200861015600827448233462428138664038851972627461175903260548893373194383862378492912625568875647273959 +9257826929421951643418775488431746425388390160227055490497812088985726596130954189607412047036836095076045019637105980229125135 +0051088814685847441269344262748461773674703937437029964258894662753422844953047676499113437643607083551852374126649110465539183 +9719703193787962011022241398606941751940065229976379345648357311141518661729448144028369831751739729767389915436519514302519528 +4754705205114290966030322546625592332320235853494013451121235653943274561055976709436804717429731588488742125670116982095506624 +7915307785043160782062481393173682663021526076353055172054644485732609943157735268019683657488875580064373381138013382280006724 +1348120551409342824081918999927191374394809490457882851229963796819461455853157081977254041113267197827298870478281259789531341 +3812065821388690082091959389908490155933727232741583927718485507099116748923648769090644447688804257458798099398350465758474495 +2937863476645119756329442458144939324030284617554341982278719802555921010666415523070911564773110585363083165465312366738827231 +9179756558387423308702990244249813252311594585173041207944901196092166608366163256489844497165091829615643175666074910233765563 +6459378049219057988171618077989915820678435345205197081776728544947416949166191536837274889454949606246062308000247423136050336 +9590694274127077084581072678020481871509177233838867384578103147792423645131300534630220077747270144036126808318115046320973761 +0633314308759908977900550446703882605141401119276147159777634200043370016079844602659915203435729306675829065339742126589258345 +1889317316284541412519941066954231638054475095114130395821846044209964998882207473650227876029465587838652109009674311273487678 +1491201614697119361909884499515016131285970573442170426537294317539659125369759905519191669796894139802704690179128614455530965 +7312991894729010255958350913145958890797364015827373553097639087944419042322472672141767845335817123220952073258598944857610720 +1824787410780276399243095677626477613009119292679764840577779945279669981000204638159164277233130499426714911295485793124165881 +3599221696151384019695918691913331345308102473844702422853474390767392532790500021180165318933884607342905862054572886894689533 +7797578535955366187957171752724494492272818965402286074464704517072955420112819202222670121573771410655986788904078893257371168 +3476992235872213742357632857624847275225481432286353014243838356580086497628026465199448029895990288276361657184717108337186384 +7666621551208709893696388991140973902647787737649797506777580191326933697405674502023865297304974748640809764705859758691017754 +6544825495307606554357924631329747091007811292348124154567821317868772060770803350068486092232254680993513246201748613742385991 +6866165375349816659160016479507285079045466715841081115993221601429358066993308321907376618487248492349626318569998572862307928 +2954124850369537534496609519161469912394832203382444510628904117251662671751490677012621979800464348066614680001698354593940135 +0193499508454379205949576005154484505543366786538069430200252986165917146991374529470556848495722371613024998552648030029385354 +6300091436093722524653066330745577837674427424963685925900231612998930766803747751089009018089480544176580489192755004987058784 +0521904612577648015929532704786690696377674858853171421019639025087849558650954768145265982042719739087464082587058116910545924 +2995811242507764873679857631100733041164546962708369112790103328792294311269786039292022283070742012101949352297665947227182231 +0598910805263795572231168128611351576632001399408367899641814748409013513401170040370795100905848171920487777572798457368620639 +5688904469919555388263250672582090539068694827337359228605791133091700320455439594271216798892203930514646320638691425342088585 +8515424974655688512862245643897078195523156507477939116050565619183311712594098054858317849757764852624936019665610738326843069 +5113156995533534391502169817952742278838176530218998980784387996273419179922080679783505603663683376521615832986618021979492127 +1086319824275441488370793880965889728722603318901376609034648244802721809098732997770252918945517263258021059460064891744542026 +1590099478966776519298307952043122519324124336024594288380180094489716784777893529727657815697554594607079223393500535902031225 +7357641282650721485620638312466056185406173433791953981763404688139736645500991900862738958018055032882772556895285743906215162 +1650489828204176805947705046005072475080530548765982426627534295084219207983407930870882583588364915870597364960371942080426209 +4961456796449042167841011430036294492463136865448894885627513655748299627165496853898722132001299392052298889154231937053221092 +9292679093929935825812492666003758036634836903177775163898869080980492856814299385978432904050294518519563887081873640579238706 +3011512315941368814678516363524198263828364895022582836616309022291899256400386653229758118495144379060943664767132561955347093 +7994582030787878507087359159314617931782658556072425334054967106060186618004693028010903789497198319676911594575116984344480865 +6353605238667338186351983657599555627480760430658384201616896420757729940462911589836581183482303716623193619087127242146945823 +5587627832512661518234488087236957525879895860979271923639746468639388295216331627000259398878042734326931204663332399636686573 +0672965511904852366825996694902980930309250905369947812120763277890579450585046143580711709629358309790006863408567461111578107 +7579999047873643551313574662014234072727914277127742448350969386406545430650595860664380236476144348769728322529154001617830780 +7768720905776990806878980911933213548469388055876623124525549575902760321680062870410042191450149173795942693780209583496274718 +7500581851674961002356261345542689400915034872242480502581980604450197467020488142845109390383270729947278076491900579496982299 +2129690101587171429651661009917402506767801016536968984813683892465052790569498753467876499730105980696506174974591421872344509 +7047018820490212289369541715584056487037206133357581281018603240803633328043751106808274944483601431153046581012102597333076902 +8327884098495349573228357110403653478827808473415101824363258200250596339218554185491862934594792362506926494486378909931564949 +1740056330174856891125669279294923651309763604203532765617783458152533228430452541997637109088581841891669514331609715059758968 +4197367157825328214419156751555885819747937310581379204615061923091469679845199014292435432070508700023932922581376409143095993 +2165125379005342133424590972755612895768392827051354709297231877589314385116008084334202246281649011825694669304551295469244979 +4509728839111272144692019658264673559690649474179262646699331625117318888277703635865926266976720285188867979085670193580891875 +7275477166128052446741208082801962510905074668975005769114065221711777393514237422266031434604625793424024653464475330291262873 +0817046411197027445752205700663441451626769878011224256767794721900542448009071309280703954949009199107668829370562168637408040 +9787763619368655981881242490879775693879040221802840519661369579710229328600940826654427779387311550329699789056384531195194998 +2723448594543172549888888596962814780076107260643457934204621584814413545214573904777806106803313154411323713368897701260407449 +8000951726014956095448794689858919573269714659606606617498924039765372767236447524498222062645212525371056330185170891625286898 +7400634580995643373473457408678165048789850829161271772842260046851336944770097590727744202976687517001530085639423877373364847 +6131960772116097152434381501203781193864171006504601138647645912125860654370952251962787439280635180850121164508129160794934976 +8194796768491845741394186667260815254416308027847649620467735109219757947751470402197659639068665099777922925932497275155739813 +5209664008657449547795035193830830788192397569777052795318568835532699855624203558168754541268446171707037486010417690857467487 +8377976554519991606329945190868959375772837211176688945495596448296939525690240337764633216663866800325009183616688088580000528 +5192787146614999641495067302066620692218333148024412938301229255333067720338121642154391246693289222550082295681213541896998006 +8136852204722481664059335120578115477689164853072624921786692464359828604653937695373728935390680120663294293493176103289690254 +1144435038869128719771735108732844672639943553994309593207501141582954078007058121278827020008994616209652065957537243296195065 +0970656298377136326223895516660489631269402730038663872646787518014244370067321464425302758563853474605729399708299081361238244 +0240779629525661769222180612134943092596901392802155389763359378856115803129803025504659400407739325825079102512268146609621569 +1920020849630289059227598433172661405103577932866917079329997842631426173495463282180424424558897910324932519322784595681717233 +1144193327801538649747431966846093543676528159861420323208804005782998653152702827377592130780075570401214786834816747288844424 +7672000694647648348909906701159130777349487715893771185782393615180543175659863092467698777377397102350383295634648499503885338 +0495630306491592179663093479474155305629241895069310776977867647781537028272341913619281785471238537339471770501056143359192572 +7308837601725546836619910084602280257935558569169920782961917685472285154488319168403367545336906857013748237755825054290532894 +9661814404701500878628706398803486793202614438164625925828662516099882010742067149392493627954571934045651513234013334682059852 +0312168977557161126723210896500911203207572745509229840888786198868413208045541779692684493190694384662556740715047630385587973 +8520266389063839477339561111091620930884162729262785462465146299021058533677470756448900131157092740484913244873616221850614852 +2366892600338849323238354679971377016007007696850190096980341739336594207986799037588279474367471064265784394163814443326245256 +4911325423638475961054407270474262985635705119625522856108931080012973303196967168739739944802380176010145279834846779349851937 +4257810389850148158544144932315012034020829622092429506782620637342284858172415547207765264086812199822149360687311593339577078 +9708659858477940738903030251204629213359548332123829955153191520032561522000846708640602917979387525987407975336034837147466054 +8086930623135121532860695594083516763569498334428501641151038532550501591735570621442476271723263173645629791905305955098548029 +2777352200022791503781001509067591047738954816120020121065054036180446105221458464420759433938919480637654297994424050557216733 +4557327979787779116307153599001198923403057470538783746410924085722270113118125208362788921890249447415394699853853075390237309 +4365884384429048241521760483369109759203884801440475321881130286288789565265074980716663859447668954490193155283240957032437631 +2400761044210689572007006009552492603834440745618264315468884643834376009957185817870038205575856137576307667033965516109840180 +9737040417279915834911486990827520959476936502177184234720809847197612305989565105760154340868284644203521456078055612096029018 +9781808678278846885397125652129297898882169803910679237997846224593685450165476495607070948013373905740079438009246883252169515 +7739852920294363425633218123401420673829295163516060432640021373043583962110762867984429100483394773437741380363822754539046324 +0883431115446791272406513984118239461183841794345923431236238292749873478934884995894017966307053473725524062094771916898837907 +6139386014396316197310075826416043562421564641768733346779156742528616231256212666391070181857228107399708720583287723130892055 +8564495834035892079724353995520557131026025067703760714350598180164418444716168502414005579666385014431317699567383147791141861 +2311406885413424305003701677451722846677377710768986204839545491722958560065098823523147478261771908820851989126482817082569115 +9602340226869859614696378992497033778548537442058541618817057710375353486743749797455027651809309416058045995383235110379798461 +9628709222162680949961060074815226201933096277151174310696525731658668144988286362519156716805236829848685383992849148064465103 +9735346812240729849346428396581006547963793149866858949294858741576251512791660305611651687839731531035722282235292660633788691 +9538042061855430905977346502656421504814621398394569363683719060626568625659708532699356988708790883640172621307887407813836364 +6305019984976325257337569429166547690694053229542307273781369534773313407694544352991644928363693848808898178142342306260087117 +1805704646504085604801764484506100460470712673185793035853367748681894021658904030806253312188697070560965442489042489792077571 +2490985236219600794455119570755743690829793064352980492197865586952595298856978623488801799239792633697972577551080842006688049 +4451839628325290929728145285465077829042998419941805433824084767085375358192064592570165051059371791323138480645291403839150350 +2979438318932849641805788421308077401361414307061802172300131888869924632094305534351210554801463479850767128364891176166829465 +8872921500436141106816138238492673770588860037153870977927285412564501357209059187194899313386454964167921543412282921398037659 +7636485131279033828460677166640282160440679145358607334279812434544494120552175807283800350790566307285400625724030248887429340 +4680751592769547019498010899871228144441828285042700998685644394131763248290312908027689367430340488843863114128754770298553903 +7371408142591796865710653973046215109255786437157352313924442478531290092820677548020730647005559727973069973416903159076053104 +0296204614919758433624657596498376245793058852298819735885655373134544548701918947319286620768915718576221199382936906689136966 +4514861015660728180490886079334753171354215683899059435960727947953401618302867137487819801076786142685334483700384896695954037 +2862199178730357056510866292501440152986864077701674864131708177930481209373944027435441136206079828523990989033623289788521958 +3232125855905003070647322986167904784829417211608519129888545490992306400854811324029764732798696566611533605039116218287211064 +3328048087467522304174357185558580322486932881453149442008314359000540253074863751401069345083176437332910631591438430165816043 +6086110405144344067589331082608930355326779125945673110136783179380891079747539440654925994890579005806127474614509336396435324 +7215091301714414443069868776064064504395207482808517829405383379414684365187710912963053526301972343897304816484149722894728342 +2436471129882505774580140273945136647748090634204279933544155848974105586771383680141377892142181597367201114241681187884944773 +2048644482475865853030326358622959819676030401734261133906361439604280224652350627125083350512410927294034793877892580606553735 +3602935472072437248748601211519488988169731855279575778400706577254567463123219123896535430970244921999769114868734160215986062 +7783657628040022836086246510234417653915945131795351860293548640058155931582229824232337815003665382949403984933860425849161499 +7472148054410673792713655531708222900140217409627089428063743914621052166172358563633841329724840401144427912925448192355476917 +0347053733535476550133949622008506766494989314867137093489295258193336447136204549399148001892481820212852414088927422900419709 +9639078047884835538046942482094804161605891002036742318046907668105442784580061293938476627159719249453272549683221805416505790 +1032786228200415283294994910054729118848671075845281886209463073179728081317785205722424406468440642562912784973899656483223098 +6968595178017412521208376863115552570593292656764798517030930445070380603195697501931025439581550142699705302804245618724490111 +5453654356413064906966652124099905267347821364144623020142756809037695527255942562866173971391249170311659304510623511853516154 +2732536262071170918681622064325048337837520111884271935643287214208402090379942906531363266640024631740298714867728868944222573 +3147285446615157276563228822132897878376378847350594831988142529113309891139340761458000809351957235529902167556281922497506368 +4354478919601617063477169153215969873898414180925342349186710519974948744199229908589593712396666250201617868945303106857533139 +6882511013607261756622150246671069745871933132400620002505947164316958006187665439907383822926483113411891198332966708317599278 +5134964407461253261959522854559873663187547708050982034442691200555341283933873911454057728258698109359806239057894517156699836 +6450666103279081547887551782344908272320062944069880747023544207556937435206656509659651679103507702121341392065845820245893429 +0369780371537429488949966165797604230927289652620569227359744404503741546226260737989821878745670653730192856868459952515531054 +3093345869807819730548713734544643718538694047498494322996726007960107180939172720629651622711868833028333458719243526925076960 +7989599203565850572942150442657633965818968052176208211383992661670496993990824695668976291478797155039216428981397014999174383 +1307119242405531123217610464790254600437026788611070015681427406612197589802556879143899518155467294982590879340815458735590885 +7771734336463859132744468610337712969586326511610847632572701236574882685603506834926412287387742247547804989759024383729486919 +5271411219228882208040644078382620627789689028134461694434130394862996168758457553833289759810275955726724366779012750280404286 +9512730467284947060387240979546511277358650894416265360099607348235188428440048072383761268322342324845297508884228837212297345 +5520192598997086640709924048681358015575115024163991652287513881247554854286841200564378110057343645081513230448973403055902132 +7020240592700662201858752560247475343913953666111892278775820432145603140381127320826667055090149454135269196723432849201394471 +5046712344141591199667738249797803701007407397593500840656021101416075518278507896267944040576213675962509419570331142339223528 +5009173314040221242742971167778009936788547416346651832329001334847054317513290947080798012432976916063888435394522306337209156 +1366848096908460688950147484431870925661107449748979391978321496249695501280906214711588783547789400280700153426360634008817145 +5032320677216082097266187853807984797656213614647275198101803139086954569491022740666386321968921335192817122277500443048900363 +5004785469025981206660221297903847696398366951138307001168655587727211921402639926973173862035209940308951165885978641411045411 +8785398824958308479147867520302086574154227898640917802025901098713415060910391722736844682709559806414137007346900318504025652 +4829177039724412998798470463159506613910620057768809973408365008034259886542242550978070824664073713620021235802691404413458167 +9261226904113620784725763761828029554734562287674270893824453948307773514334280989553881807221303459863642843519335273228474128 +3311573735232406552245381370523373424363177967581228254334269537766809380288366330679697723673023553333593300509406940840178257 +5850353980111617800710390835159343131583882096987317262767588967118575624615788759557354550955443154249258491730584290096574346 +6058742867661098727133777961247323273097355895095662383648934730004254372357455111922489913009492093823561131792526688980983778 +7068373836217667990225856363415691473206022105532884987579074277226993152918608006384495892970483440333181267244554192632887695 +0375929236714973683664267906774009903162875936131255110025871545553310661098253000439303508717945000859600253447780364023263921 +3589236909622208939924919255987333064134666511205037102476749734554331712415072770445515704120917930961110839990243914307170158 +6795584768746356073781978623124211251870230688353778395309754364333319536742360245765209901147398777468379230149142523181377411 +2729948487677410474357762288343380948554393380782338431844676175646578459942584025062978984890983296713845183696219799180782039 +8853156389861615819589926505138052323380550759364020828050387019448769426662074170464533329827565869171293278882331249180195752 +5354617268287092363055229579924827016859348547054043349609066878817933385065188678139799271936447212380584467570750380867131343 +6669574812003056132820086160683395897628895296098843871784268115803999250621428898234509785779912888254113882189729913124138323 +1094710833263258542617692112795417986473641110466966207723302828445199198995397058382901854029835381782441946019335577334721714 +7084654187809403875061887873785967514836739155665927380077234732823032340675248478794617424267309726934964316167994411232825813 +8586810770368457480021270214945101098418804122645991758117744041940927633232985858835902473466474854825024480648895385708401183 +5596898921066046808429836093228050083092077217562084369964360104846761170405526779538921555848456229448715495096371173722462344 +5660195590159993019077789825004164832246820538076625410586718868028600352830449932992980717485232030870587444549082470943172077 +0821935416600837395349048851468072663127356309332941980901438793730151587795962214031501434310617749617534269164881521735791175 +3042072934705160703532059769445721283937901499649463824595439982424043872605434635393216517511777007087089174560510963232009367 +5464155662396168936608488040261163576038165196682279135320764015816698209508887367606848299565138365679605265583581996112669276 +6753095562768549798586933346781184629087517744898743057935941129179204236737611864578907720852163937289062427955626269555922129 +5716541079272109791401178359438691238957049044014297693164836859127158377764717929820749372762244199830728156908190474636597721 +6048348633690063166546832299456572409599753235001536610146572139811087134501881675589119526672410909520126024302026020371798099 +1171674402450497762501699459068107617561003253876215131387056282446344211385618131395030296408469586922564210025675216461987907 +9312371022806876440175927558224158477356755411023006311819103124252221341547172708634888791607917037276394431911343613744399883 +0922159956723896116809200304280546821592880397796462717485456950002852867129487923589665229233372270334774238944990617682919310 +7658709241655408916654408381196074058493866541161875193933692025333882777349795393761970679827330490202099389041851596493234215 +9234545178442642967566968436599668141535683259923279950462781030512766827071494181783393026832191674347556257505316419663487762 +0041257342453564273568929735796453624713683399848511617479340816490608890963355058355974856413150349382759095918170362098854394 +9592779304960316507865578967321355607760068698329045954497816675023927602619347677723250166350520531047186145206536424990428337 +4299316161376269795506896486995922307501710625151467203920818965364151628354223037516185750241653836563366411298238504451597789 +8841671421275735676647633262528681899530392131503705721695449652045887039670882496194478043622621695748874619557992717633215233 +2073059696519439089739646800449388322820261958926389800326850308700850275001529873409140035288149114816334962577827586268363961 +2490360047432436143344657376283381577226551875079136121637392911221073093866166045906272906769849919522908370281254710635409730 +4974848754591262059988003622570276863287575164608170178580822963208162146400835022943896925242423440936618793540028102479004702 +4372126595413379264526984770633204109208673880518625238463206225086878596008443858616399507133771134539070683384873925072702058 +0639171273649854719051709551224607779261350414555233764709993649110326667200717789312129165461963429321431418833445603440663689 +7186962822361873750181210266660607397813719262382253059434948056476254707273143420389570046116876618519383113357898774377253891 +4319529017033432347952439153798524883464591210553274851928717915148566352954737293867412627976883192261494139098284868083014649 +2622939111808623590774562258070588172127827495395694013730379591966886930134915200669202327099882207686441261515920355425526638 +1200973651071533307294108492132851108586204275141332635739794068695424575790972361088170979623763207458716935295604008671317983 +1251374850775260697268191392326213755565839271682628582402356668458562455145193885654924842502488021956636839087993338831884363 +2092679910172237700017761223090447686346621171455515944807634834063707579619074178681232780539980362324148874096831135359068248 +7059799025589703414119220067452626516006331839767724497907056535882707433149950178268364082722639297792594500281095862497786970 +4542905544332920101592075397986385645511891063856851014878633174032148021231620025882083670673312205077519194573640381350698919 +1312255205389657370322617251904525722465432117955235178041221031184821368449334584954069933722375723026079372281725777733909489 +8940234530803425309074547064952811642951856360280155612932727691967562499759113316867977300723413954039644096858319695187621995 +1667713304364140592650059312268785120415368020836414159057787321290823207568631669529330852196553357371986318924246623685192637 +0422089184234642438098080557484372830069143118691288742408958852833389685002956551956492581208496774233386094905265339874099483 +1677835356907666147956557529262732608196197501168021350247232245111279150809917571509855702652690905689682229523319549019678024 +3645155615476272052329623583943381582821256380614894465459876556622453836730749021655688760168012160726745987877981868221817319 +5647755640891329597098392215119820185325320967799221680909508299218493251878104661039386657485694867555315346235424508828545406 +2249500944046395955203873004372279572553883458895334843433140571043474669708074473381457065865063499709405786900325552248426583 +1230511579171467602223112925195512272958191569060831817009891427720423564097069405239471201694564617244826682098552152177059273 +8469866721000791525531110152439789939013289745066720962189402891838096512969001059723536174778699525713458075138245336864075519 +1296905270266717168150361331577154511349585629938472384230294812334459038517468323643620870384471376627488513753063864443527971 +8484849516117452587091720711551100043539596219785764133452403530020825880462282298393651618450654255243582938022557870602084123 +1944353914349184905366261486479354722961723711488366658700425467290309892874206605011693831609609440448499484127167958439545979 +1904342525350793098590501390656028964534129323617049186915074724786886755773131382892183343708636631327982725884104881408443775 +4047731221209068512782993439144246721190758070966515666060478086425710216870076434550244885851035049595368742260028400195490338 +3375747312957348876639595293489234210411377963219380216708693519698209655124852272292375830483071612875799792380567345928238534 +5110079232697543396393866568868109799067673989898575725771625847005572590499345721422620239447760726870678880204127450730489419 +1892883230935369730591390745517921975081616548082333381141791553938700817410468926449238904528671037503779324573001732065035519 +8395077376884962568002904176016991329810132526312963614723727626088091850321489438757392596606168596638103913778878129196298132 +3938927636631738411085232552592623008752473475775738216360990000223836611551924314564820835426069886741001153829625505334993487 +1299692449183332553204542348863840181665273246414104121624684776356638436277872632132123576236177751436546351690643540205371844 +9194356489958293805818668234623245496942584158879649161401045859846964460358471563904833418667460716546730472435403875780613403 +9275256836090272445757731146730052254532930338266683064726986679677894399506235508548851649563290194895324537517696553037056936 +6401315045565945782629076626066813595555038611734944119511046358367624104666208828448443470577765348785145599426526721185590575 +0586738484454531185743029468713911469676776854865676690022624209621140076731906638884334409090253652724218159918793965233464039 +3171209020852465810305033445747086308509991604961680580404820907479024353374887015602705890939142087700418451581794807689385348 +9424311150932643701645157338987499029528852262438813754400403069738023146915551968014972578206454343730343122265559473474927849 +7742146609950233423816671939328483772283214892322537597846916524424589673152890736557038920458731659652032921748569241119859976 +6687533180189782962288035952098447575892236672807319984532899523336137857362773056256776453970744401322852572058367357860586750 +5112973080188345114036362904396465766939815911981737447433859679102909113460170044475876180448426521815838110280974934802651453 +0141366660566071877771992446357332649851216791119528638672163150044856967323023760152081614645879961910911227845879654515023475 +2214123760185353539707121031098580866690882823675580600794715047520486504684525675655152270550399135886572417008928998130462848 +9223973656232181173590003292639341133004977532862200821270596930208889426180465089396056658959981954090935260888619248916061940 +6623578698318166136431412779326115216613003933902509874343492190835346614403255698238178964137991654202739789058288364219841871 +8291158969189181550110191401002122993337707939239499805961356987899937471416091612952104089354586248344316163672726189677077009 +2569911973118347742318591298716009672334442600647034815935927622454202539592391699029450660978546827587095337413939180709898411 +0899648100113650188435064875802395140965508050973328163902047496526339571954451436627138016940395531683176282436292339394573885 +3218116539223354243511329358754444736017570099304122222814440304223163469524526595019625005310578387024223128529473465323727623 +2903847495538482030544840551895363794476928236443136637630260470246965452066473119820100467512281726986329810864283236603867046 +4226586385557184936270267709088518321173404628666811633771213904835009960571786348833009556180773685058946093743512399598902672 +8536593915600232712348175092402812063615932808932457150254760365196790780020038688785606653890320304533826035350207715944668272 +7207307332779658122899268750088169215451053148485733154400660830345046074837509653982716901490942833776007996597381089382249055 +7354364139715042807616100729619187431380023428907586266528433249618278407719620456619422594117403277614144426686677487237152240 +7463017222778211045789643242204755641148330540930800826901784405753146374489626927527265867849180494980902054821139633747003818 +5151230162674659769071431499839596583910959703956345024479063722159584629356540852497161935430481786349464090482420412307760810 +9834488101241849496636150547202745300929538510953947644624667132590235877398283748992791388091916279125999482122365178401663101 +3799117737061812716778711014651789301773605485731280987694315010080752800132070053513995645594283497896886041897544784262244652 +7787706667573783951864085352707088626343810856713494836743287556709150843482692271254078127357988781374166954894723733919964351 +3850833609175684557920955809259004150766406752693874551692602542748508158101511651908521148570349768720243116655926752800025156 +0542333256075861408967459343438031423694963841751597298639419078331767590705140401244001636510150856151990464720632445051520472 +6303430699933496407768275797041897577889994294810383735293345544688537126863233465001327982318529841153751728223029693637728347 +5397568768462052741831355049132795323985421528118062025164276988946171985642590654143451292287062379578161283874235728279748519 +4640901822963272018953105436900070403241616145463711907565248533620946580204196365483196723416387548557874992063706490486368853 +2187665586968675889547896116950223441017696539693810802819756256217188102291391477467930773393197219210849036472348714939348493 +0530870138541801269405772003173391922440292694411582576482977240988452402062182324365937231322664832700142793000599915699446377 +3232764179605713808662558293727950897506959582972158149863534091415959234408922619772945504672047855130269189456164558749700864 +7976387712997851037838832161690498586021679479533477214488890351913481087239877288703362839723676811186594991567854176580725967 +8767096970476797579593159059506720944428573124512821132135870916546309651522551842044412513318859523946351494542237948488449896 +0718064204159475781113170590472579807866358541733298234646266805452325502205676982790448617382183104529620072495546016035798369 +6656888096932816475747398102488120987337320254949271905969490909227960204913812078261626642629009104843808070570261399892364887 +7197928827518406319220242665874355187232953000643123084932578105682719648228431872207900564999025910692470162420939628457406240 +9390054304943829673541677221189814900027963471043254648597886086990795714249446234111674340418457357710907778877224567169903636 +3047190706971324195888140547475741620358462920833528017528636158028581644462796976018091635090361132164631234849610385434473088 +1299169151001233913629789222426928990506746164121889744635941525381099603593944992448532482489564134843216370953945058018816372 +8022886634541463283701288620853613352158026264893049477075544241214340204041747957155846007535087712499320675089565208063975842 +8518974453812680488184297781261482966404248280615326837256707090751389480480211355208009414536208955423929547041219391948623885 +3604467721690594055924832819820324481035373705830034807313911053016213471052470202123999030910736617997566930544442854033269439 +0783963633104292458209399376532444272248160361826344666316224873723641667255667675127680227315908535163742986352235015659049712 +4004639628899564002311837850986616952858773518487977784208052701702476955042845648511721265262737849283604073030798668836277183 +0862408653011082563803132060254754932254909982790619138767168477417959797920610077885997837256974272446346326081237570982197007 +0940886136952306043500899870090754863364443344376526643707265847999796477272121932881155662360934605500536289044543909420287159 +9325468416880604404675394196786101658805312863903480479928341990160539724867416343519113232899201923506345096672360143197907320 +7602604379899247751883206653536848520843218205588950289513511227638017895858870330990465204694386615676242643330003217066315632 +0851199846447079085502271034855714656668178101657497639831079366789502032474686387227843449343312220724903806454747746782689424 +4098848471040002241791388735422021963275960354775258076923928679254165040693087908221190070756034164623860222301010088075103056 +3055192757629057465255631284293545235726639464473675957498678493846951810733281325747646636144880693592596312740548434768830767 +6731474905682791005984276464714844713588718904361218261812425686803684106123652431919970410078795392818111582997268429659349585 +7924410389518202685905806688713577803625532077879713115752972677622209346192681858566234259911076164694414416704969551453021886 +9355683562980634479815919874568756218379958083872036318959815690363034752884522901587704723069489938561066700856799286608658620 +0543220462343036193244858851552821480362000689940188453022451557344919396300863983139599553360452940548134852258624017234867371 +1067014527356580183151961289434647684360706506035600010429135039073802230973345888131418619960777100132646812026883383699827970 +5359069383361932605007503288578185157851793814788353694181498218990573049785293883804780055838358139459377587968253742572072043 +4573719136273381835268729540845328454994954362222395612112318303745157807658681724375200294326161779060096534923740884933625708 +3514458846264455850674231783280205705825729759163794635971944702176098831501306476593168602881301267771422854514181550204581778 +2814589926430879599930014687698610766303495462811227979670379853246555977897078815634814137435467174275073493314846601851573261 +9109412328394243903990091864894855454386078755925003658916610663445020722891205775398782367407839761338086985543481077581229638 +1491723826525165284311575346657279958318121038102570973778417404071686643040506005133105712555305338320966176072017676784923629 +5488218241739200538202629320385686355121297434909655261539604247574676965715827475716615737941607238002608903015929093309168488 +6222350309549145806267049598652344478433516262239158892817022906333942210072994901250459132730572231739319182532518293889630019 +8044072077285353283240535057997955717161542654506209603492744952552847242899140500411096612225095709028876598587942929972397072 +1885954490526928165806858149219735696470308645981279734404025197278823077339850867123073835557226495747843398970587652389656636 +3636990744440216604699149114584326945110057025978680402356462092308935861145193409488632679598241716104734129398885084544095857 +6069902893386332364062130016921517943943574898468298687528180638591233629526536463888804022514538460226447545474670300837773440 +0715242925233223925302043184620521073165032928562060926764419538572558168218946544243657541925010495652673050247406203961490742 +4163377094941935791929068796922863289449570870715728363127254502097952823099544701660693419650850191571787642058768071578191335 +2362149372830570291861457450525257727016337864275693314681112878356465000993320675510813633779009823896594571255519867010367760 +9335339409871128425332395511671431500329529218909754194982323897857550540930935004728114503428724192561034251245345901549129065 +0756506273973719264925319448914314432556471353593665242663682345436151888535592338801660197397976918922142540903105362128344221 +7296939654818049488767067190718755957947538340790260392001857532393622868997477129188922872359726386124323878376837617352814907 +4078579999111217939011699307760285871218819333972182125286903838312055662520500719903656687016759241817857273468272683917688574 +8726333004719198815434960721386959090224338905967680668744954339106442434132414526246207631805974218944345895924595868424575128 +8809968439523768418749746186037611280882890683322020074536396621763557542013569567593054212148112921735580176883820120783267348 +2537069038616920940158973428724068585048795501013293376681740631186566951229973336427931288891824421015980577224523633941036996 +7974808176187279818828936860482106588007126950454089943602952411737485839990023098877207814888492767150658782003308086338815157 +1588659669715002646102364063331711646691061797936643322785359527528662208821134749050337274899095692843563066296876088924734707 +4549130202526685872258479111021810868210332005793914145311147798812593113617843971880716197778981371491177307945521311901222788 +8560297570244477829694687977784621455987068942424983136944870380427057052887779787702603030649524780263375021654142024115901877 +2048774313776039429808088171712835186320571466269472744364892701622816406146475324290364012640387008030349061922998372137187474 +1958983930518040048400877463606239970518078952389442564587925608445408847664043055091202257080066087803944336882062127350978604 +4065348283423540496042896789726757274478123900698244046915478972359449728574613924262102889566496871444268574144572205450579978 +3629438124450409423602990222911509881985430704078927765904795404045938118161221397917848075421341126252805098426191320837834521 +8085534923914835313209677375813349364458599796275600866316693139878003025545882010477316042716780306226946838431532163667037127 +9898915134568321964545103764074604849670802062543079353666823915979509779221620608508897413582424170944308180311897168957048017 +5385323115399849956058889239701492340513864492761797303099763383900112373871513361204005145800948160917012448658958645510687464 +8637322858113048873422361029279103210946558131854412009103898341572774795544194370639320402689943405658836056138282160542993169 +3301482949893831756487465487869018898293856072780727019945397657149862423574816057140762598392679733606145316506298182753363157 +1989885295563430148607778298379580655148124775805236404809749482897588166275331919278290911157248463196182871139898936725797368 +8850340804158989499571102548588275214111587736824329752538481445797164191835592201791780416442465525643066854658921528683230761 +1421812723062059588410998478367550671697961940333154251595442093624642651562126033425248558504164225223871598921143098979968917 +2477995193068866106116650832975646638319811948342255800290956070421894468168681224145709091760495793451121243995417375669474096 +3150729646482352886032550446062928871539222609285098660402450856512727724053427921573737445175821849100475781418189174380995632 +7046750620615596575236877490285020288321290904270312430965440858488622217217317102253600290127052465317624607416177498383288106 +9945695686299042380678429305385240697039293151977430358157956385467140053019250720885367675692664936064594234820657654969335744 +5571751022714308898152825948152037875574841245733361004625842517428626308101356793475497881525020804091668118665745094943275801 +8608762448666074656196662829963227928222015907465782465504699762597437156318531429585635263586424089541092855892902609967867982 +1978685830753176969518395838040278058667840236176435782603960672407003909222439483538760352906908927302768494238355233209217253 +7074071920319953536604510843365153876170171633349491568232092272150542222706006804946809770825099582766752656052960983718114235 +3280496115751440895543554193849398007467023324006497516116534251153591572725745514291959829061315518724947165745450390160259001 +0601737046591989613120616719919874653529282948250689745032606820130735453268070027901333295387220734852357272804293331117523997 +6136958737937467562116926228263651346592589988767235293060122410789132148757897567424381946719333849272080001742385628150689944 +1891595701639675010992493605507619576155702842803896050077803395482631144748035073236453571286662875633494080865014296782405059 +6994138350962374748911276067098522195781999897499082597900135054301540799702368264182765273926228557006482725202228009397664985 +7788023609935244035749201882811852725625300846793646185552122933557882704452555648205100150340311907075711931570895497802594450 +2197653103798752067286172607092220822596982226247423576487125070944191009560709583893589326623076734376750057424185701225493266 +8863132327252007877822761700057804325184406486939470443703245734365105772001424789573894937476178130538608010471355446757855177 +8174190110749058109791874932401725786307915411703003043709908238217181652029602262116068758446055873309569311805547464496642142 +2490958747068677929992898459199669839140241121204680850335549077174567238373970308977898462513690749136086901700597984427253536 +5982357462083434284507903055152940486985159078049952746161034961672224995628192794860546561549979398991180455860037140923248338 +8926976775587697462174304166383030496921336111150196335640267063653243971405939737115496867091474614902122111713955464436671112 +2328015592752422584686538755738383160041067711192111211555671470860520931401458590260603642848665758765427324349701633338120285 +3238367054704111120178059574962944110115171398341904274585267575082605150464279484097073060481753901121878225454945359042570553 +3450237761306126967240618521993384597368444622149529677366929462149782561575513795963740446321127465963106959992420775690369312 +3448814725588587245567368751607825271329254275986408132599484784341832975302652140521158378926626072460485474580935278488412601 +9150780220021887436734602033408383688852530542191765389359753381850912455739194414678686576674855611507802645590418679170889751 +4835292344245385826099978982021385208338079311915794842181613685383986799333650292340449601221579759524765161463912861431586003 +8364091375971775721571844301438463624297900164446658688640976105663184901553656708363187917609334998277021278755741801258289094 +7084708048206234641953289421943297010794421803966656094223814335341165874897678668246105857901156960186097878571917343164253048 +1580091826012825586670694920435572709977363754508123881333362672498821977048126176623956391025928031884064853593246953559407401 +1343143393297933839473552543351834063051545756389274017156011730389906161525821950360013682370064447544881706475110217230969461 +3302514538150761859786668968377489515133804300482090273478961905590177116881654275768644498073647264106408418864420104945624333 +1455642109436975199690456185370628722356663838058282076416293074142932077562024273500214654758558303856335007961702080005015078 +8063031532965823241531821249811544926127012157501395220835632529512891602224510304171587094059291180949555426978545235398053558 +6792060710713434110654251175014016166953632439786838514140951524356192499726146571336067500056531834668571621898017480292338282 +1437695510093265077864080367192602710341444418961408220405302548941539890702354912153612054318858614633735009858764482449040861 +5181414865532338468286437374705876616670683310417100729198121447458573292151625305538384979133781114543503822301014850960361423 +0442509889959385737920714766510641471461284309092021412176656794047214218226892073311469169814612693044014585717564971417728218 +2325595356223658506960436649084028083363095352384800259697237546709925440654926883086116058532378827751785516901620186132035077 +5272017869427934924377297497212161665902064352062568107159660976294283182349751217848859566575424801083760720335160297859673425 +4927647302447341405487506286250768152116103802235113601574433911840768325925117428685709060725568588745725091307806563183578704 +6009173829226300709392490503960716312095637543299322573027471284752881663572097511651811613598957587604124624018835545870558611 +9874256128717490827444943646188644499417848153072863603454092200052312279880710501340245614763095671533551439274986414901802322 +0712116664532492284027602515056442554560965687688409213710573265303359796914199163349296608736358741384041138558244053035969858 +9693983191087651982342027390681525334777714170455444776207288727295047097906322701667199571113890318007616548536163726817679168 +6706523778360875477183129443881169868743369990013775836046887154518569535610947164990958856446973388802060554267647855673512269 +5862057420569669693068391429181291253874208771601116624815603880238130385298427887023332027652265710135551985724073597417285974 +1151313997638402328677281725415458206631525714244607205307139347412188214659490398369065819541976622293080801350270699351750349 +5489131825943276548129703872359273308252153537674522058679810049913479398153870812359217912596783251244700635734540612732369203 +3810123181289945953628771451906051867522552261199661987069529884844103683969181491185606510174695973406830793843872394354042162 +3346242314263710422512316433607417431578705396179309838772547308651613774189871178285455805352015354652802479496815910335754476 +5032059944587174601615131788722847272253927881072336845872496308794095355330608135957953660775615970673613290442946086962710905 +3407385896995114869865346803277241560730078573870063366755416777507927736959074993460049742600768481558296737228998922109024099 +0049052520096863687692070927701978011226142548782375715516453178031755305702747877694502859869129537906539333534520649523091710 +0991259574063512483397592194908683392249793361680299501959035992654866098958575700878621835330177239453007223378603834381079687 +1103177507554459538813745204571902700244177530864339036101223775180048578597548931452670546251696214644617674818880606435247986 +4019388020763636749355438098141001952281422670022244785922521428313256185545192914769702955966048643204981030086298329088086717 +1549239391810219717095199987772454895177623386763902046734720379092060908409042895882464384442634362486868518995850543330943745 +5263274887172899674152253042181056688293450964020128773142784973127071891058343458622006778652971493625047746423062870250661196 +2492300927820520335243072396524887229850551524757145264574209619185242101186100663595852135831547431534587842715149723823788454 +0239821705984933412196203741296576026949577895980132509651253316722691651055934018399445089497754941402505430183658650264000569 +4454480470480799484331207273048645656468167612178274351303856622687362159020726843169180682079344623827123515453025370828135970 +6142817374030811769491227966611452974299687874240073880283247099440633899922286466254504368749938286010203764066551001971357963 +9169371099648374900400474139097217260717487967105154156622253602791903705738661081630850020115390238371457432815879031763979788 +5560591117813511863922620504148281644660212448276865905578433181502848473767100143776780656617049797235772123812344362645447507 +7125601127543091335948203843174929841142906765885076202058923049770681372235738838189576737300366872775686393117885907972185950 +3813261893105186097817193647097680347436595910522013775725325978428043833139417789267610377229028363642103442485430761044994912 +3166100516390908023193075423476442023657276364366869002050485177701831320339505415669888608102030204859429175627661908828955978 +9494583112215459385851845969134725676775320276319447511947728695272882901489693777569547667119292451041697329224102769821139225 +7691777515996491255498611230620011375291757353871825879292337748953866037150788019954259501731583992956846734646612549716437169 +4674403391449814677760084246730522440833433795684947826674440906657230316614951017230444639014782979009506122819959837960185292 +0124355369267161692186629219979251945207193338015531285271750882020294586168274943975716404048435047057151424920598922068881892 +4259039872889979839195103377532319383985569925201669042943584292754218542022884377759029683915124413393419307275904131879991019 +1735669223208908684334277224397343396336961848404316449603849894027168642444303310792840158007116435560970914275701222063897926 +4461905512399641675513869110628312166872040099906116555152852234330620387342830937229572832948858700069818699059571737043133655 +2317566091257832110561031578973018527337285656514910017567896208598685109912802746001639928928505435442403312983994465299665281 +7100315234971800925514182498331922330287575769352512401970636582041663941571732261396082519132641394516031353060487765740093218 +9334108345710950934819716799864030596011645738530063495673802842261253827926601866855793893356025796840470669373855061371630452 +9938632519142602121728449903028585427271086908360215670337654867014624403654411514574585082225609074457979619906343109374814363 +8543685485409092161128411354085800792705928451605723409112677460751012429082851977989992688721655128865875431794910678786635771 +7286092726668793850700794820169050814700948490519583438078010079869324333131287831724247406778611721612540484173599614481822185 +4574626770701467258525092815137010178678255624898097602082268698327798870150981485054197815103745515005378748184630266101601233 +6996566956298539914158437280054873330111337840249130739283399579411421525420812145476940206933841167306808654076518530796691101 +8418268977140865314494741099167445020028244920345037125932530888607409097769183652295273473400544354104335924214164055029523702 +8653179301531871439574603212631671179611383475494609355467328013854637990353997237551580336715104250041179882078593885288177498 +6757096834181706624090949791934584516326051126444323415129234296822035888300651545387936037901447054120865724936805272897353272 +3719471070090385271298581540906250911443020297189549719410313905300350268160333497598330190662838247612158675798074496367653467 +7420546839654007142424912212795950734732168508855699265941182387494625335487354878640173067010282119575715050167662820992299427 +8999971488927790060730948508800281979058687995045352068109470869864835768781397747295222955822196732634803342790733054201967597 +6576480323890189237090109618880316708942469888066595108911237671288781962755118036472198419023002661197238821372958077703913188 +0030541257153278168215715641021711922462243607936192442241352641161941478602829260934238679472069149540374325351015851641339748 +2308535064234691761213175923993413124441933619616103905759452431159366446936203305785164404138919753229165645626046227853031827 +9274311478528593074600453989280243110143811549871941292371158642466286143077576823957560727090752083042241440289345317446749500 +1173344584450748636805732688311608479601983149113524409946668094326431976475189882181347248492781101482302481266734427266149348 +6919145353409181506683417908661000530570384104910516382895650835980412723990294313264588477866084060873540703108364930450237118 +5325748279790493246685409745464007302387808804042056122442284885720787803880628218787879564860434934892913815793329423983574457 +6453232382761168189611119093517182812167138336965744983593436983776900497001902946217406040709180061277827817549237791463409689 +5084186690867471312419665514325307095801242831316185457562663155798586037028408988187703062546161289787145296742552136882463426 +8870611874224684584815641540909217473850820482485529973916085066540438294865034479488663791703681082228740535939513197426568394 +3829342886981207956995705990631766515300196466846957375041259104048073176049856454632700433183856168388096468289412653196954791 +5561500767524576355366009997865394697845244227185393733933754620619491997202491015533470742012384145261633852618565246889880329 +6777844984653507188325486982839438152297369628419739121210603276755667853986365800165693953505799574103492924940759456778294070 +2214788746856145315547663790110177531050862742765653751073730521273798413515040644084618990342819820992718598911810035213540952 +8641245540354058813605493935866324134857506571449379897541736929598327494870227097960497570120553215541706501704428439721082223 +1700972466803583171420254007427289345043866532653318330426834471075577492940413011600530531542381075895357213715112384747031546 +4123673880740060770118806009141779021375877044026069684883815887385967786493701519196377144556351001736897028481481645941513408 +2662387772790420942409426074953672031980544055539694698764834339968798663047095391766181294233657281432203121874861027827377503 +3000685337077147654086746625753896686864534986031835702363775205640514651662788785826457436508900020141827996232430281971022030 +3024908258360292259670809665500668877816383415506517420287409766113445399746700536527732408780456759980633419485517682977905050 +7609884167715679957397855272558744514159873182477148797463658401514781751802711120892880095270644173320069885204433134067923423 +0014100455158046473463596055055311019866188559065450226001282429179589867255611231640796556419411721523548855378235369309657647 +9633011209397874287442661801560684815176053929256216322999478244794557838852723961384875065741787167826618848105863793521919749 +5428219888294008773249094303237486065991730028127620139643719301859855449270217745813940272539972852306476328220561773494939103 +4521537632739898925531910531504909038824924540851609471635849282948417316063275786486909546139512668787750779344864961591530650 +0285764732059028849862258838048464137495797174589639632732738487543884739338580280553036435526582504329979148309470441850901993 +3193744331770081476790157034764887938574991726583896870321256839758584043852505285610942913104942834651377543523173599555843275 +7417116084257040522072018694176431604709041564144995596676144624737792695410277079523480490262409478745371723464489814999444924 +0087200564277539488709914626916914001557170500699327915642055990350944184767970585140183066326636283502929299531353689075738930 +9142572663076266551791245195307340496553949814370587843903642543965453293671993338189766065010527351493298615638788367806472270 +3051117521339185890745759762693550854894691691802102995243849634880012010573871449566457109934687770743470466412945533888196288 +4158491602681925772695910708467090530134341332595220736564785569416215771589193350272179369165509097131151416084829009479740145 +1103853287929082360008599627015884781258703830958532256506076724299192350494313751000293453841630584783094561339455205657099017 +6669316988454995197629701325242327637911706551201433838323166461015137492273818007712193923739461699066479249555708713191227011 +2113634008934694195638896909191311796132499125975665209093925839375449400491235035672664100786917876149509780668632265414075514 +0126792384563810279878665722622429577480527747103593555729429055584348852183674740137123973052149872227487163624179929217676754 +0429035865573402523285948804441336965668748085575713483346016033116457998360922288296258888389027618101688101669309692510329301 +4327425318090553450075452219201170395108911828576562899226660479453870050953529030012653173886753094327406771671224243820895023 +5520772008782057192477714645125967892355088889936328957302124838926954775059515639556003228522011635026617680256545865946919398 +5091701805891827972817338370300267783615596474978619035655959815481776653782092336443749908777349150397039742672328322075757599 +5790089502501043722549854203882262801131152082075103693079735744546403353282880501020065196803852275180952587214373981006637291 +9796322418193084084142426874342342820014220995329829353218013607479946651207097461896332568716316883980489998209799290426511423 +2380307550005537574650358548246683232060877572246000949898947779365886734692122861965160733025000170093436288569747823376036955 +1082935962455046112099818942036953428151667543776795511944294244788427450015296441783391903107987172541886534905100146388519657 +8380489382633120509090625034073608336127658262734309778765867742476489991527857117594934853404329609845740341120593238746572358 +0633586457978329098387174811695256037738146638558272816343120362030421967763476640034051908578551893496813666553354240880945358 +5373098216766144042601726925416970539214195693276289721208185447267076741060153916852700809475592729479267755151190925441394762 +0437558873199426030384531618231327018631366219163826316141114912440580402838580312314548478509711368795320884000345769917178052 +5639423930316613774685180001977671844817204570499797609653987640525926925309564084656003072725353558374445587925969239833796829 +1210135977251946489323174272650887900867086890978166723808871820517325894679293066521051888521481265231417131690626664853728495 +0539462898410401286175566033379657439604282033567012883413399478842279156974842259796901368805265470365140797961756617874554244 +8867613639089241484274514628778139575529407010929548231466056716290533770512410612299882134379199476168691871070113322417352240 +4833451141363445314543208606007459133355986460223413209331799936622495058516670427933156376684533870032838911569984108849259357 +4227813092685468088212208364841572112496680733745196711345105782294865361331232982776705201607924522572931046426758698077037314 +3298920484699256928312414816543062270280813072616261084031990462638788100585936907797010649601422888227353971911450080527103430 +8677297821634129486433214400771371293863980023792460946229497228182272173859417196509074761854532992353908622744248599410546426 +6781620363930103389596792660725581404654123168036180688431615736861252151699433810032668453228299140054818608750957825124796595 +8122906022184441989524946520995601380863784338127264956306250128205898953341684941467474429559908562531894365353100792345598520 +0411240004527716081891095406341655736648076045216518268298111043258024354531159970495009766409513979584877788837540685928510096 +8110610731150417715423694936324187409013994345563727775716872413402896219029394300366269945656385788061342489896665541029423618 +3755150141442065122724114186286854845339699980415816422951602029278923264626041281480497555316143381278936597056953084064028022 +9997198501283363781790158682287440631540353151583792163912187892594225150451760679251039984200332891341466144376776153588743411 +8117278660244194155818259161232321467141273951036898758016163015261776236723767428479024530348209694127274271155128147667928805 +0936167378563389999940015556970429817708525142859666613441735283201399969921725036498088504412772933143791841603758820099231969 +1224164983307664154773003073878449221074064106296765689183370554165942272977254560731754387603794592725273703193699452227243648 +0945223186347621257574665017745770814803043072264123465865881332861490934432664674134678310921895127370062396231037904388566491 +5578757540264948131008953288936928142006135647005490612913881156326258717861846071301620618495929699330750590715333343381570370 +8766152138213724166896984751062466183413369758646701425025924527217037621942282338912065834670044626031344011383552551288896568 +3053487311706357675549287442389024758844523549667853410498690516184480612259764698839374235801509026206625335813997095967736808 +0878702145089270046099454388880300081151816870001049594773880813882330822460954086402671799804568587855965385027526964053573154 +4675804956846633737319204649690805027179329770063541964923158992410097311744090492573918964775287855196910884200372242948299414 +9493162547793763389979831614732404846665474684277822658912090008789913594349007683276852208398902139951370774691434150042517692 +7345730422542157756414350209374237744157462270676494668123673800976571405254566784526649041229590418069453350314530822807850416 +1964598394787617389494252382959463826235528571672258679974212194662236832005305369294264438079751870505403383373187745093885686 +8249944446042573821740194113388700642890510755384482816288425486723007601353010761537384611960083168099219424928983664383056396 +3337126174819661239061831213486837993026986769690178323635308622147431003839607372786324521326222541843059384926020501381139876 +3550125336344588588098791695947139914704682212688259894589141433972450950443922835504195392482891358587151935390940488550833886 +7749712101826086175922145824388719830543614893730681609524662863877518177721578349626789661635402530736054829364302745002369020 +7052231140791673822400576065079175225485411488088090330405642998541294857834801823806307475957421894499261138427284899379872160 +1843292586638145520787104685467083303977045950130084986120058743550949902430669077350548793220932862849879063881005931348774482 +1487754184031131275500703273842524528318488976791651801275003631033587218395976230647938697685183895874331282612016119932243681 +9443865463893265996646234621765924400556069796282223873874118161895663217586723720163197972926787252465189277090874867985305943 +0897354453697180183636362963351299526862015366834373289147517835099724224465741319067052046603751698502499127219361448464402391 +5704747041899601180624074915530540633028597706644514489974845238123048510465569828429534100611326273262355319658327363672387706 +8316996307878784269659645906887598359322978350217418291053466278096200407791776549885681670136754711514051366460206315185892069 +4722848610616728633370187439001656108169740264355894513804107426599369076853462061213905264840303368831514494760256744683488217 +3692367810726295806823047818393633700597950772879297341267237845287738595298116096169937443486203130496785989216239853131904645 +2667811751880900529395751251780403233472004508704751605995052039171718893677809009851286227285227934697321953881347551922850785 +6909280644240056221753293916337855667825910341176418724960272141306897770589964342155201798900696411923242239316269753447770119 +0999650808847959816984860421127802079006195715319055574680057716511026426618826912583374854994252722086849395905477181959336308 +6682428165643537701794115175085151859549462586484758294772142985146427091765603916714315410145159899524436580396471513525615313 +0287601495196877948543109041275994870460190783889596253917385102642407519701386785758190101150573237633840832279432894588540853 +8953039640516871936098610139590123074456039528834700886251621001651688622097083336798736244961699802438533721144359429291546962 +9090712409265567652916908423800558494486073728012853735882650836864723820428807816418262790577395429269519877994493798687893365 +1660175388130474812877157640023025667491901812382385258466335889928876299301453437354017152844077297406456989761054934994390342 +9934657654239690864626478281461559020855371564678749001531999572350068313050489634121119934441378970500982348402296361517516343 +6359086489575862410458280767790738382982543594459454924194612177893193423779790100018577922756855011303081967830570557786235066 +9627300155446988370785806212844429529453154177930814144914555470252140016234923123986998575098139584073958387735640106379021856 +1431322355214385540867355391544675004031897330982334142717560574603867105320387323557316017902469957357722433291210791176477230 +2894506203039713875024969465668820869708991702780944843854568441944017095189870471544531770235169544173059692732034123691090895 +9985087463071783533894010510378579436608410386134175083499511116916575290748570942191993555978994826375337547024070760036740444 +6261306132531269467443124232293674590069389935930085698373191334535132795190967480627467212037239181698443751352939224782894445 +2120942222786996279924477488386308186164427794112389492451650285444010405583566731254920895180572589153915684428013418667038463 +1430800636064677716455907326084896393465425867531959255704442690767435730899236888138775592453451717734356391997983296892276722 +6245887023168571979343600695324609549057431178812999538431044619282851448174071578424122279044132430998592688952289021834131681 +9674276988668745718949139392247616305459443871778443070291211471809698194880071407948810420513382171773670397147925755783987072 +8396960072488524616931349500116793025065793132740236508100883299116095620726458675309257154864629749532773543158061852267047338 +4708002781694658659895872218532749075225884113621393683821853677375696999740077965880387656889715948194663339565410394344347289 +5555870327192777337829881158886331270254424879662205835411641384434648950116158681537009743933205450514200494453854527945997900 +7477629425593340948832945986684831020436243430891488737957650425170704516063535499519492960538931301090427592749932503236484184 +8423876423875909116240287411777889735748877901032042101830839646783823015629217546200038215020201848216827890202734808048558954 +0807456230133883710789676060781653192722644571250120561750361830812875165496905115518629620725251203869040049714454953862738741 +5303451865971911125648060457840129182131488705975454818569283523145391216818437057554152537823305765736049257100852046148635621 +1261116302127532039373528605959266607418243283938981652100937053779793268699698358833723678904084230346993115051100507293779300 +8426944066403361349421019695464969608724905091684569968020665596067769625988182192985084507208329554219894020546830342373194912 +9836620096551496246720748973842622026481410313119306152287268352751048064398407415273133860155815754408933010180348795503748753 +4609338756911699625776639967309034003047928015509904888467025680057353339055998784262880963136510735792422530687128803685177960 +3794220657491630690779260465220147420857430753103256706506076703567632175129351004805264212916552247643663829004772139169350940 +7434241221131032055118140342385838086277269231509234123959703170231052929402882172699084758826526485094213048561191825363736939 +0904036155468367437756029547852649525568053491591721559536888529587622171812488273446127650656057211625693401063929504404510727 +8594239813337093390867462989975908087174105244731466049861585875653241923202495711120654077327719056153590078128352003621088112 +6550980939146114945715655019872060022635651047970499718424015515740106255804720532248662461951505100400404921280927158680583839 +6741404050546354494349489969507458195999859785965336806692031040435145352201853821004643353403220778871762826594034649052903693 +5050161106286616149825562707832775275340760357578872487646716309800665185049903572794595588208600449320559114964177323489795050 +7988668426398349050514452498533331732900030359537800440340518464659495823291497139184592287414133272356549392805616744235453149 +9266075338762306748845630416478232159198106165396768656800162018036519950819645985549720852139273704724379986117806399232175170 +5556988932197330115132604542704979085477903108338527639954740461587257180160289420796745117217674397152069207476864049579397740 +2173348172420527062990485233356407054265180085263061116024667765176353870469789794863489543966815025332527451848905334447400043 +2653295629344700441742575198328366052220017722996829827117810780652298604720896185508927603921863120335461979927552660891307644 +0066285895594775046166427188466940243950248922228033222148293913898651489477743932715196100030572189599370661700468278120150796 +9557220581453859816276169005234368536133383160153382159372481458293494645221000658600106273797363754949138070903261376602472730 +3805492311138707905865671027330890704509145113408439234581134791375788711343962876719164349282530634198140214550594966178264550 +1436926045326544742314469119616902325996600826173849367078861191857896132772006881206929709595222480758410968732831090627983535 +1444386260983859263563013696700221044836992640755935489313754181471481261753184503838050091200730279031416065367564840157446166 +2902149047863838396186395637335864504592375088936920775765866174611877001827681253531321463980847446832448377620176627133555161 +4791558390249335499772225358752569718358080171845842601137108723166037557543519329054572216953276760772287383669070062122544693 +3653096889815826831439877436629034472435210543189243295673175535474384447647824085044069839822212271287638158109858092072312186 +0748574544351788265285261914954044165932622710335856937231283900552445994528620171010664109729214838117784033934235805145199914 +5275894527081180465946457660332530286653207667782652877623355615488795343761394166684000047525776778529177571402318083997867530 +6467109418857304686760026137696284413841248686101454431645726165291910757652786986378480149853820055715870646485922893256858093 +6676215878298696393785441082359744916852977084427450114208277030042915360587042453548139647735771416607502451093388273522788649 +7671054706470997891661682369520143839129305382788490682363534839812789784359519127806453517900921204537766515545890569275307893 +1361042207361371441527086792533736691205317414847988400835638938786015770536113548366581806919926001472840811854920801842703777 +3513103534581691803636903931367504112887620002748826537288400922168108606124444688399865918353400722960622289440592437281395265 +7889386408564819031497565924192895193912656422791697412472168255473833561450568882333025985344661645666990267705103860364236866 +4138536431654239332667925068305482627915123154662709208870044077990559363503200410357003059370208164876016094801255094919175222 +8463391696176344840026735185830036495571599978819527559041511392555529527154011417043582657075386914403021244017762572439715299 +6162318218961162791019379824579408557803275130662824155987476456514181881477682742571553987831058318649000691189597142635269772 +0762970133488857814721372272166424064692356325030921377174309564434883115428450738561984323237757935361350713032425828308741123 +4714351982559489932236335028957017819906944707422172680018267561533474486226912008423131335876195206249325093723564894293512080 +2816500946584950779938046762589915909515704971018715869504032011106342871269261795583577290967260307340084652876419981696310087 +2895004364486838513889537109545822827678603850445221912466809759993491507875666091568956682275842225830234855225383245575493552 +2650099647818037615116956954341655646634963643235750472739733468509790968870462418042203958941068786618484819431710507936599802 +7533481270625343830151159727689285867369942408696652407015806894633372367355011778476566950994358633971847804532262002525341326 +6932860309305588424128816753568417315152512711756820693574982607856846643933251360676478873401876049759834996395397442053366771 +6368418934202491624332453410763014824694322271983857287392943327020965722037743354197029665328443075456374222598320483575376742 +4282586458471797802238473382178433687724169447427314412337761093512867942490521429740324461721167516599618126259885014134320111 +6677394682606606653900300409179091142196198876169170096213616729601352901860163965806655227312385454363306654134273762118341113 +7643573234932937551595703701897895283801028602846877296702625125220327139117379468939484216431057135643790896852072143295568124 +7835389220407382114018498622358193237717282193191181508848234026347203068182480302817923964772670420825100195578203841958774014 +0034680494236515185913110737330886189937723492058708152408147979713114102686546086347368499582981596157232052021983733385897893 +9959931900795867812310134476649406402976231824404571628106712844354935633442704883886435745579659139240624317737165693276684021 +7687653483916295211948300957846886688394120859824086212655110896478780014654869246794209169764271870284569926071280986536129686 +5389201304796699165935771717834181664385244359824248801827481372360749339940576454594000410357902778074011200874064037958889186 +2726930691205319100949599707692925525547420349048657283658532712722412081334373160958039199607974848481872853703890943042973921 +7254087398978104571457211604241039805468739560240953011032729779871018385163199958508492807104550542120182914215024552352099374 +0577915788776618918085672242537299536604399259342051743390425673863630842390420280176568564228989365174973256614738413491070038 +6830892093137210682654019600696831217364160874783778871977883927322860408992703263164973180938595498029716242874574648385670449 +5774283228582139254923100205140724149936447963980034063069342493084827233283799090727386532026590721266521634433539903773672657 +8489342339836233604189107412549422519166707342446243631744337172427688799797300485425305108241084447320396355017099790987982304 +5579039086144583780301772101084268454284211524135896968194358624225126732215388025582474957280102865197930121676550220972263090 +3389908879281737422526309759732063914252217622801815648828128392852505613143446838612931228798897371072027044252222238376051988 +3571583131629499703290168358948884937302565954157189422433659984916252703476429662770576623285862465399735505261324990848316694 +9423186355018278041568884367207433805709684366572651006506138520185260823230823570755489649379382727052958581331266615555167678 +6126444225712076374139461277391085492256424369818965208777930082776462483927136117628590877335005615182912506580467844672001776 +2323186582401204627524733122349298471005163415025134564249126647383086521203590734419048262603709759435997087128843414545136629 +1421280371866708001926278928901318288694248295906455469958649830300570517830927222281187526547437638030692046413330135004977193 +4201689423423687675024635448407327285565295542200018160703811061939134316388397382336397162201256369493787936488029791585101767 +1265877778847130342563633758037018164444481581498122824054055477037485608751037801526946223069038643514687690116234164514587643 +6465410554863527094647350795029177204941109231775452235798798372912591022108733306213831496272836341289051922220644943607370174 +4555709604071919166868299016817656945294419168762671940808165674695761877170460702219178063962280901485563918842254363745508105 +0013210837712837766354170359690869396554185818181209225721015219925108353810498389109811284979505591805687297968690234266672264 +3540066879463905504617792447842885657986052603128773981944573151451301111933677250259888942146768184470087035596773903177095378 +5526250295384032490751656419926139482444274162610961490517202317645513359417017299610996672882236901128269120411948696682951298 +1147928498844392902179227619628807696892271738030552398135463750372457058220840887917423404648021366673468670510142397314290816 +2022158104950164034675577261500173802135526124382488977804744045573154829053227880877248106935421683250032295002832455111826392 +8408395848784043250462314109208469565763023368052967204488715596813441318603486322106989843882974610129619714497130039489786739 +0214994088989310972413925550111779902382656332396702390689823464515504633177056625665996413210331231059059122014949611785985118 +5638643037266704666895115401268842632588292722777701463135298698439281447394022408579092978979422708178801226855288373349007705 +8326215915178072795379523340039085009563169070517125775679474479863606678664704404360480187047960485640111421757696617713004548 +7011793090984794876958820884434550749243106407550272966012525399145703465211673094494166304978135466245182409167311669917812035 +0646616697055433264306514791141300386873739412778313053422926573847874849306566154499488632029298944379782617421929711377514401 +9297384752555069074761141370510516463922575237795983591479132397584337422724053423993525662073888857524562744276272922807488244 +1675900193060201503933483483504265157956265823054822690157216718534136770295979836197494827206847083365905242438375193125219057 +1431083489513661032602020638770515681536739853677363483613015298018233407875797334225693452983166033993589897236847768729569036 +3412842920266990322594256412420427432972591970945387529406395749977856430023496367881365081011414676715347811993981995507454759 +5814144158348481510698013052752762134884248303529348153673229587940320030719465454064415732294560941869034046035698546780721067 +3694293937312762744233869259702927434016315955447360677690945403418821029256532787548058243155743890295919743842535871205350185 +8704385056392271497806649473016369833786145282019863224686988090496033200166054190979749241314009880507707963024916288921654292 +3157093902990992972624660747840907228349704051194371097267625916545775966277296334446546102566702460108399894331979790861994643 +6211141708601791603941445199717151439636201237385208163393264036156546491266459438443028650233309419884755515206198073403412631 +7609935217879726233731421923555596716242460372189351557516117275999330506730738459524772217152788164129973955538645698104965588 +9847824808961328798019186709647321679530290047204083126132050648182784705034270305452693472108814807665149578959595630309221057 +2336424726766825888588931956562955321343032371214572623650620016457504060828907233038750693736196960807811513112341914628133531 +9997503724107344142689181995651896815056436454295876248977561163594537358114242360861284248276610577578928061231272901007371912 +2013755472268039887671228509805013770238575064767125406070391538875450803622634669600191763159687914916440803301533482646017516 +5799981996920369911160876482866900908859555470100550461338722440379485926728401393736777675182480925560316384661340355950830168 +4131746820931543634876854491169439418699998977639325418048790558796894160516876451374044889060607036066881936080985211558198717 +8071462893187814119459304307555930509471933902061826760709069595504601694208191737856286699434878770893482761009503838844594596 +7181730908337307386590819037242463777795080011670425589895801753553643404874388537396185083285326538843075531064088612600268485 +7874226918011033625880877484768842271536701038369116084490113476976040281425980324472775723714911411152630252812320962593721057 +0414263983887707664017378501754926805713789236380580564348508099662975168539697702344553508537455836440798954696577079098427990 +5498272997168152869729083312401990047724554350008563240202328869525129660589507428406050419409546123885174117080636577540661417 +2321805983098426879552953451815149973073351984882483766093075912871745174124737636496091875961776384678278562342333189413446156 +7532047930885191527458749856036064187500060909405487710845907268217223665450938676158247000950256593423837838865029236735406065 +4326287344295297838879679103933460283015103512394382866450845885799711951741393486898208614864273534333346921541450681108663424 +8567495767845161425037712088656492510273651099963049803480674659806775332933320452431001042997191274150089937218301268747409008 +1892491166044214033213915009098534855928106088594182060850901874215590307570900626843004514029616915218847005161443909006676086 +3105173474201156958194039816160955452881468075916368040511971709028208813038518932050264340243550030216756906608101248802881284 +5437478863331533890468364414134511741947560918971502005022258558696334726057777326125552089511046269814814736730962698056311073 +4951554244048858298258123156812226947705933314291630536799208713986095914743504621255604040168912151175487398095913442501527517 +1612279415688868683265625113331706654838561784474685978138326507909792993504451472859581305912676732125668231491237540161852687 +1352117401098609459260904440848555453118498774861101544577811081137171667682199549961162875051222443028292136608108908775783291 +4668566015341904293075901042719184643021339885314119600028221153413672044545251474917661767760069310540295768155060202109602728 +2520334200236839012091693839792035322503068072072460065591632021254758742371667902074341537861212278319285847598815912703706243 +3175268051143981660033747106123282192419159048635214903634052864839951778255031756400562020446144172393222917501787673701002700 +6674523291734748122422773809147614700484318146175088254981107335895144969807349660283900516719746118645468637821636061386059378 +4188188277626583767786502184322569089569789412892621297176431619545847378700138574560707680259428721989759555839556974186233480 +7390532896972001905316907714622971854007628170763072962479615247643615622571558632228493177244362089074929594584800973845536848 +9849264610200952816901406520106384682505741852721568226048835135789532787094701294801848704745256861395407908649513920150263639 +7854370986367091444291966220372693018093730764321979930716154354135718272983430773038337101141108835455952869661500155683833074 +9808115836250545782744528663456612751035343126746511715032833222650951208562936847133894670903159578406935434899681616157157677 +5449933772771897148631641377562540641640474848389783754029100557570032554566006191465792063723092795517702238425793412416470001 +2937905058909924731783049378468003437910238645835264792097079136587087350442777867813702093887200643761896159097869017005937395 +7717133729012763683884090295651337537034023162131283487583227409327777370699292346651312872193008211062413650051860746533410200 +4095695950110108518764643338298928366182339223897361738328378044503564041168430011712729973754089101835569129650304327287404972 +1174584897511466194183788253926349029362049505536254582209201338252575420290164983455933289840278231035884030712260894346134552 +7761765877961955439161913415420485543948776016504802146528406434389546215574979243086360938551004600037990661153084242444222743 +4353062554304250531566068529474211745999775668076123932253052153101156786485645664269792342453816897835171506547074839704777091 +8193385707206057241016991296332616186095885707895014417445978963635638843413167453713147741250557894417823842378892716883564383 +4414701258878541571780503656512173574547903536405524255843710113697817741762054938128322216548020454736882148058230445988304376 +8363544420911047180282206501238156287142986596377518728647856328365181602387822102287895774281073353368828111014956911375392578 +5780430022747871965685716392391425352083934583867099701698503567269032511748940082003282777645389137257025389413017452674572537 +2606024393701221307694529871431154164540614164993497271619958131424791186768644684669356636777831879397734272673335705649633002 +4789715983933276471694982923444249864558464851612286765926229253919876317314926011029164829296322542092827015562884359904206642 +4213428153587165381335521523932052241841163300973801287138253226845490672931816978773941803990440278669979647051465348624079105 +3419168581475572983260190361701934633564914249990805271700921787735379441310149748846950460105295646015883522763651311765718139 +0643017584581435034370880134992336308617187531872808223433939732921528795419567765739615646487503731718827467059811489514462708 +8240926821672036119484675230474136343520006840157742512260506574754560864426113591474981060698558545401924390339201368669580151 +2180431409945375285121040004138946753923265119496541829137171528887590486854621739462433884861201148650039065347599969036034946 +1220794484575763660002325630268586558185893116790653322219525972500497727914800636242536069684827121496593553311794674511534108 +1435781606190730034271758074654507450242292934136120417582018758847558210750517197211614624177861297537749923102696299059068262 +0472496196020370020276610485121224239313415267755759579734483426439502592786733423472402917351281201878422928096327388672118427 +5664545446808704741371688000935805171339458791507069541059893057148797349299303009996748288669321167515451714205892864440743408 +3539189653746007637884781151821846130210806166359445842675385724193537198834368015119520365512286626859121785638758771673116936 +7228747311262908816869990638505266862436414983330364594232199408818881369887998094729811275091341031498735810192826600585848478 +0205817711643909986528303579098875467050350065610591795091066174715779956393527864740830714556714738435192795203670003124014759 +9197293866434029742096641108142225947219962019182256790387923893087254917713809995074288755692595202295289706002058647970706318 +5801028709225408662416729985040929435165270531028696258985082752391299922713342941633843800570338856731347298583819618303436452 +8554584149353583626116529209271190042567689752495849141906628210792514969913837494587223513509196504949988416482688661169416704 +1402557567228850127015457900783688564010064141030969351494178978419198543363884975731636660451573493841983931009293074401767946 +3030096749835569373371333947576923588432247404291305005146406298391452901679290153566364898274791521766815033709118309495674776 +5711698251707518922789190956913393869735623141004406498318629780886567378932420667481352170410684788247915724815490088304482484 +3076891999474110399724898312925906240059216711193634437769661170118805829580169752019184852464954183024109801959695130678652316 +5549698831705307512844145517641207234201273500862193514468462342310564088815360136277681192006596458241962992029526454841238730 +6411622599480809121036108851843289420809827360922656655734346202711742438003990222191272251022816208079113097900823895134384085 +8642218596518127375568301272202091207693967578318544987425186514107159773089906580986790334784827799489119999185876094206749612 +0920817351118635692704580562112859516048707763068110821230955827967708140350064043987570026933663880446183554485511010170742288 +6208781018246003491238792622187493762169792217017386868912187285653248963115594278272915569084370195923738619149855997663765716 +2725282513427928049648973662146630492856487618068183730067763515357833597788940914019736675580475997314248706127026547890194380 +0912098065082008833983417148631289829848433926717155189805055489641556714887595710925279228500591899600062727405015074732933183 +5895616117549258380703685066950990019495929210886386708101945826998030626417636766877015647925822567530097432835311405452851781 +3061662816058323226135782103379493145683394691280827163116839270882202901941521202335712663514020076981153846416619848715503675 +3704213022599283410938645199398840870678548274677230864636710751729466573816253072905154246398964963820890925958414003660031502 +4630926004977081614276851598024033198734821505047735799361816301169717640380369512425102474864033576302498992022080746889954455 +8300690807062719013236716321873014112140959900671073008576129360776883572345888774946590273926184154530838752149268349654124927 +3447912234767016528327462063192808290048544233618790028527492173810076784214345126091690768646666027973848294720038771161886178 +5457890600658613094704911994422834789695852986406564746776631261869186614541778685993675186223192485017919263967183543307324819 +5220338990377375643418843097310570471194246573225298484773451484736001523372710934922163010657077255170019875356114982042678628 +3627624529201855124798528837400884425278033564368552915095248122583917121126951781145851039073704399808416096566055979179720996 +6138215215437842405346946457258803271828305596332127664808930703097188386621950424243497121232196129953435128118553466623324068 +5920381194124220091735356882290501723032521874921419228504863143247804084726835859796865644318358484363167764149683837216715113 +9615036434361215242420376195665243628638184799766269042140156331651635843944014951375098750172039065463946417568449326243598402 +2848169979084951950982695318867752277301100025960923376831113692149385936857810916874319944880663028251022532340529140647508871 +9496200698078244875645144567349529530108679183666387943211721642572053750534478253516851058589093730399207634528878897274151362 +2928780676487223500661310045048476864951396027388478482164226318809641027170548978608486524888062863065934362652787878241515138 +6280140376322091582258435509210383633979192424770386485640747341583226684971908322655364759264755383681296473387197771187476598 +7993834385710389281257683754961049197160221786960013125265044154150064439440391405850545737192660609079271627770663100625583357 +6072190114323511020360786591498148692349156157375850747362774972374185937176303811511475665210532862701141979885066767496308496 +6809986728266957224821129384937931767139546739620244968569547174693124128355961928311295430759387663715690541708892900136725621 +8713302194814326105418406542612287987228114780045964567511737165272440568730052294690749670110950977545749571399774020053301568 +7216372973252354647766828421771297596834262561708247359782436050128141372353679212726164448161015103612893738264244644610968102 +5490158565324437536585310937019467679460278414170537463829112562515850928229747475007728895628087831498962809803771938282971625 +7508575669851979183836777758657815365989035447083239066619058708444094473391492540134970134555418935400444033568972945852753572 +2681051665685441434594259287440247146082524888648989023037549139977692642794573123372272447282566487899476245382067369090850227 +3516664349028783594652935471979981657724225297147289075312362875423752912404004312370632615551263897637065247961679885605652690 +9466216134195188009940680229231563219004828730503835830166900891639648098483491041893870240357033427709041628218884025561211450 +4848574180102172215721912201870496816515688173491866267900855346017402326765363320124474261873980008204988494006538252079431490 +0175523216285771758146998384814407339456495403123042145864725622594607160861096627528216923235632801268714390775921555811885643 +3472489136602420352765486727018974617090254641528435634640334942425890346384541194932965110314503859645146781896767507525832148 +3797532143892746774112201800929601204074380314303366575611389855222496601937222584569281001864863917059035591510412512996095572 +6540831912166873834974788036724952907724341969947205585823842409980011722639587514582627960169448155988074691268777322159243577 +8665277823830969067856126267665294223334216725032631778088124483157924821285251588520602045895388804877588758409591955378022156 +6073508435137376529408576041907918073738449051031545977935509368280423831551346121480713109837860058648169729195905582970167345 +3067341878979134838523394919669124325563435281537910461171499830549480992384643061189946199980549578258740383418503347009079843 +6535435940186811905537955923044494920097170641355108274375772163581055863892145187878626476077935978473720854007118125956368393 +7039488100826115931812511833016243383119511518291750301509797980336012158668128482275262947277553272940357496952990711563149829 +6786417640541461059254040553435360397444650993725507452127477820766859236159070974204514281362156361495968903161994222700825800 +2231234046323964842602361033236684311038633571563965762668970125159431168316364793191351729000958068187715242930876778118365126 +0967020148060045963966458121640283348948132407097784342111426011211692372274414489985481236783110642969061229593384194970583663 +0518863000584387454760957529314643812512823643579550158516331617073251164731368407116086238782619225015253603821594850218277314 +9500480699512272865605970060364505765826990178852739273720908237913877224017867775678563171957729835114722642208632758208282247 +9138815300684460564222550599375232818221500256241474012816587003367381334232108533309664763635997507997023730828738583077664669 +8788277264997082732015978733262501251796460091814731315709354222338224334551918351172940509755714499856609104221032539934398062 +5201268981606265439495692981057225390935909458631770334121087316181673897848115792408678690299457980695904707677321358225662858 +1753894444989509786731779001286291244699622264775676367978135041545186928742736879462156569826451402964970455781449463407937916 +0502111951096040580120210611615484977765606052460541731989539917916976249725653281972573849533515652151626503088059881052346467 +3880078154575290219658231687424062098862063067654268460424536569017374872824177081255701341783967148208402475928724455704261768 +4392256036256639333393028072709593018903870460743710545351823219115495656631234924513636506499865017761998475934414247147614073 +9879823269518650692917107025520590107178762204459762006129828636469652976104262657652867426597078219790167485035259125893130526 +4491505097353181156857564911252591317148670618490859288558887139051313551147429010258650026546020137477020225229299116254389267 +6985649712270923660498384674089852650962875340575045596604964662128408959309248980968270692814095597628820086983662450615030773 +6856393844414418123694511010327503283724147879193562418774164993500731367874976787177535887228795026180482876909284579667315939 +6933319890504711538399213021011090982613851985557386817639509779738587337666166285748238443831950809951451700040304637246662798 +8972237565184004138743415214250280650420991574154677063216686873786654244381615346208802365774879184715316421935088510122029692 +6620712954831004627906209308475373401688370427674946161939930227860450185388905612087218953668667006558048824549749639289702982 +2252979368901033426647735610872049783979382409247439935969647741186385842273253355085952551816588123321471149874812539836048112 +4296535534263007024565979896369915411490254323267398694533004344505948807352166933785377686738429645888455485219163542489241753 +3740204196364178691705296568637646938043711157073683455458837069839389554673644717418013786210053159123354215579725572298341488 +9402853270709855032434056268150751702914142055269816941745088351607954708943354365301302743628246326922883750858759728646811815 +7702984283703968225748705248163911320353463996186650929366431271533557842658056577524133407573715263399635592058138266058468047 +0714364441200044139354320382443415251209148277388628504182113543224388038221246572844030594506529807381529435302712895155562998 +0439249856337395882945730885613068512496256701244515252923706394988734280606704794447812748091162906835016774939625011625452587 +8701684848748737861671752322431955447724661967607517009813604790923987516158908955881849157986515334551799664962438730731109340 +1331695486624815425190823239475277990544123745272288937823732446789801006674641059010522745250663013852871097862751638189259629 +0016943722810447477028299392663967547809929491638369830715943306970577569121839921705532284065788504261357162084070598074698898 +8301370408134947453196729774027640676030589016291539644959237246218491449189371838582480346507102754824929019477369849852385466 +2141296007314745921128534279212951822200061786556429858733058886029876384472876002241385397319110771995753216118435172891077520 +9778033995372614700099511429265411943800576787023724043391866778854759353346452408608492285235768924255415140931221126895275706 +6825951183619818611745057594180574765846145064255544523604725057011807870654883273137368630387060066151211603237021909384345606 +6330524686796934177420756525152826921339672339044318925623881985016143335928837573713885535605018904858579244102576335089585010 +6476163553560279805504121578981537068558170447489713068606370855078339092366601341950660196338341527489856914865088585338744059 +9336566578276542977779339876759708356666986167810967015943735975240831465492474205336898814725322385265950743359319117386285955 +1003736656415305664502391530550831810874960043692083942495595913831106487788016650162073804988081719551806867379947943364891966 +4573789346107222063252149996048100164143705560186510044433851243141510350751823081763496713992637129016266999061673393254758192 +0532366470306788734368951439658028825164454629260106890282620855033982533015451546843353901652120510130789111458392221784617757 +3253852880221087799999614626290882768972817659832607861376923098885595639154736935283503251479471872499871522284582447822302040 +6285682722317206257124522813577367514121775302484636697810948128394195942996079881830875566043123925277038343926112033315427349 +3775226925665315936530276286731605375257672911376828473612175726312446039948354980701402997357293119532073936998664168131452515 +9033989479402366425600648174316259705202840220576987680353222246016330572206033521947195443476632332503203414941943726000075449 +3497459952076058462828783375726894554577596495151945532419592454939988268844456907659575363299273992966202955633347460395801503 +3425070812671430759768577458384150050234482462127557226191091262303333548668785285283704391650036078067518873006221217224813250 +1772566105841430765131974198596141137685703304393653624186537427347991201369580105964338526460016536799639038488848966636423096 +6770176829068426450217510320589346020757715335312260267538371974073805419798173049507293562584393319422383700569196675270010649 +8817107380879373164458511189191184591118659927137093795839429896643513855977855984835472389317216929700259038397397174527984545 +9182176191236023496232040243155433201447895622909243154991531577802143936144665255362367572046313372242799588041424257757065202 +6776868876338528332502171518394602792217092464624165442452676081627495348032268487133922812426314590277917132179444733559137458 +2894505940700668608956696393712351416879091173479440509500831009675626499007967467374789971290965256917798968433095199887640804 +2933556414955828193417995654373126786599609721238527340939127371332396349156901995803794360944816870450892560704118441975220867 +8553706670591731040619297656286157301262507708170329674504933402137245881090232925455937213553504830839742590855235282434383116 +9795978189578883639204205303262814903090980902741535992637415797011196591002116065669674171784371634354111486560182744790314795 +4119132103638186216017314646897106719838984255536083613903341238051086509058863316947216192276628486590741349320633205188806897 +5670698885913664671019828424294908157556037863822378418679559826760134301152250392322845741422553340998938758664406467756180798 +7800330341660310724142951110753435998016854748165017611258286920601003037645494210716339815788133485140202482683448446213209716 +8180605035942700911937960726293062276511453378796626425148211321573098417580088171144007867547815289216240544844126601589466966 +7821272307041782298994133562164496850666805515425583415056198384421212580796187662587737326754702942280674718201253410570348983 +1042240816866808878998548317060777151321655529169893824595029339588028328148179827446446643616423322057981572730612868764050015 +1252481771070461052389437601673649482439048233841755345725688472923079857771939132303119904339949419801565589936559095363036112 +9690992931777916802696331919514208983516033826094247916936120742724743873182994969726283601645253015955852055017097669830871832 +5020330232719825823684760306159154637346729706124058199917604069885798242234996370123942691457916122474221663868046994104598933 +3075139912987034850456516141324884611162536977850304751997334983975217864864834605427571083113425269670859785386338318467147612 +0588929507981854517277972648054247858977149919377574480251256787460673116218337687478964763305472549592601599187647039007311159 +8091499973422866102588159323886136790375349337327388200287148626779716184548302459458465936817938524771802569873742301251650117 +7114931231277684778417144640504976098202413303636808727117937611520910337365455884518757181692362707318939628346107928380267487 +4950235887683950624565704360578955553783208046483127644086525832912071037741169842458625817412278583829306158758057435134618696 +8199425955265652644399992914133529916068954186792582922740480043066932022166380883075792995446339961281184510898029007416993672 +9600726548050421231768431078919064425306219469503912968092554459127359674333330276775477837154780938957377807571696713004700625 +7295599369136487293154800206051955319915985280570186743302107975100582206721744471004980480977594771538574498512856521123216578 +6297798457301239668561079936581574605258366525996359269984364801565322122584365843225724581654618783605926367931879918225442452 +6533005631078137948109852121400341144965267577414971222703110484819122046478368287561575133762804019638669026806894198861867537 +9875087635268237028446411260770380453643015454432015279985047995719605753073640336797720945754884595273185611911133456865738261 +1289442162696753398201423924491482730765830984320927644525084976853458311031303193411990973143614781325043459014068347046089289 +5407587059442212092113638721767282263392344800170580359252927060347500966261590384053868270413268344224069405566567860000139705 +8360711621827142923632844470555921440996634627172367848310520969393805873503222609811062308946619951688125101610027026987846081 +0944723049476073716566027637737569127176067300129222186002435041592350356420231369282460258580648007773781925508162225156920162 +8475983626059931171149310298021646566523963848825823192493400102745845807108661239544077702836139330088359628977689491846874286 +9167471361458822389556369913209606973474148790242038515231761016355472551050907573614312829544645064709238887157780557241782741 +7014298247554687536486236050770971039248187073536082362810060976896826815113357515444053263548215504897920396644014795535941249 +0560999088691723682614108804238530988595549473233071643241045493910124491974900926784898185428410219894939857122959475910314078 +0706992135879838500099998224229773341412566822617554024599579881313516475013871821845580144033844736871959630619808210884015298 +3257284534077293882786787542863570138992146936142087051892058183105267975351672517356758026095332624891123361507959805527300464 +4074440343205139647276844490970010808052229358468787479224053850708044118534407822903344480715673998402733320764968913814756460 +7541792444051749131437550370667287159419463038893712135130829128921018730253201583423068101308427818790061823168162915307740555 +1517558628903132612936551305596370414201320454890625631435775037687907085643182588609436062805330988597026150223859036793476645 +6591140085443467002438387440547002194892167702007183254656681949652141186889776360123350307460795672547510863464622633652798129 +7586611318404573147896264171755514255213812732114705872658268827371541849326158561820698871403138855140301594042187495345343587 +8296969079616469879346007981374601146907653922591351081026313152884017339179855411584129459367820354848581380738645777750515335 +8731520753126192088044684618604970364839081511006806274832524720914395021711036520236353757973882463981033460910456305044738117 +8522729809358153390546384877141395094866223397312997581315815377215593011496352023911920483284381829852239261360027871178466434 +1761342609588366253125631150586581982113849076981180786682830283076537811478367306351425774976563602440271445970009753618288192 +2190966255981782407270090701820316391321596233574351800067587131465490412933509752425381764960420707947936543904169527628499764 +9513655081411394282745221856716659032453348163674130701896927299389413958771625248039619427975826651601280801943472380714643137 +0617514295978753057337209914173385759751936634152085643157967813155609425512280471102235144051790721763863647958187237685008587 +3639355491023500583172147594486316367021278514106274611239107265098592491987675254836995000835549078267373162122839134981244694 +3032030608995234928881439477961757003027720355891764881759463215674265954842081986273329762678139617217680974433220699552082278 +1162323720318256232198753258939220028085022540772566418699150206741508982674558652349158167206478285853624098497667218452569282 +5449383863141380776369123912608347050738601579475359170938129811063143046219515664348459639566915361115334890006657006031842097 +0352577524785516785715741803748070461694324222115473415680064156677430016348738412256655844328208932407174179588633693022145069 +3240824351920886083704779764410813380504193208597077988968552959949184540967132284829781189340533621232738907229081350464536533 +9963137564898255760485048365009768861444430624328428769392562608655048396702090989488644412080235346086820241525013306324575413 +6313120896902229376551555163469139342705609183907471905448131939609829452611107992213294654747615543541094148280055017927542670 +2193326928610645174628017287211712211408683079421721839813537852939457890561997238125898536070007981217325358023811789787011723 +8243869736268481951401784440593310926754240433539070013976127357814953175386685024013642745787559590618261612069606519053423542 +0179110493828168312231149822817828204338127248358983471224771340698766448231962762617835559498180861959832229135237920163613151 +4787386815157272287656433720722356482569671165537977997843012106191355180546649834098738516604856452092754246704403258051505371 +0634701773994997694376147550495812360818423051628930021511577756465075343321034591427451854134383867627150784580620161049855432 +0766676598258381037189024267219785521248498516810290597640526034223344261052531930836169758828566004912961881921058343147697444 +6482511785125094948376527886610692241647293945078604787929517099856009510498982203730650348969745313708621728477885759515338493 +5205196085155489849560658125068733947071196666550006357643461400687947338189762369344420067691692171235090223284004207347050790 +9809626775201561107404043309301598988061425083419682730787633868247584100867048954636838021112840948572297116645919603622359907 +0822943415366943321888436141210445288839456758217953163265370657515497810970631795622846252681104767475177503937782564413276889 +0099794234571476050102251924980186262893747705880229668101192044145818046711997376029940219465691893053496893208450200931362078 +7338980161503676399103395907119988361285887220551606875942870878016149229490400212073840391368478482151725734739290023095487410 +7899326915683751786630813196982092775796371026481722729931635095642264999233728389534905015911812126591048864589797672830096696 +2751118177471534985294175817313968889734356500489672942275914873801559720503551024017816293024629330339229860581633938440765263 +2579409952433198069628120504384999886282779583918285844878077794508256550262226622072460752726666294025165627679376513073930940 +1557518549380248705140615993858146675367318700594928368305022009826106738355680320673267541599976659073767016002316705812605118 +0706655774414337358133841048392051795234166451755892548200847217249090290629891786004054678210061272314883459040459769608941760 +1894242091014594788195514987048397793320863358408872612791768967887371355399390803287124928045545098723504490408131463760999333 +3060272094521186414251733828941054176318890330197109603090531231771409739699564694060282342629340263255630493316904735659002049 +1028815907660526325417480678219858187627208188992524766305787455603486273947607751634385261335594552079352038645485771077029993 +0683468157651545576501200944305620051155390867906355388005127965874177601579306536442110166029928489048442610212141268455617318 +0988081700313980006604864032962026305268670102163784987963985127384885587927642407664223367758558519122248185218742873483956807 +8435692954315269375984976106264988536738299966701776101047442064080401887709348151446333391726616352591853741366988778921917957 +8956328839854159854532673749984321247777028190876411555620913589749860701945785300696279975495557163768982684990843868186778431 +2520325381475441756157721367873710939223796803781885424158254911912476533014496257166233162377942526340788173572154209732321202 +8180099306765466453860728219641256442389031828386563717730489464926442454808958172470108839488494541309400369186661157251374638 +0628247148382602252920534144240581556722670156430583076620252695866327968997749969112676595494827895760196984828212360640034869 +1083401588117745555669152288505435653771338944376475705488767101152826312238533625308441022273664874833693060276780849703095095 +1156085057274283778724094356821366624982478225088443694900412624860954703963723162086763699442272343649549404340286403255643105 +7910121814653271873529362722249047841930063953194714561480180469523505989148782328178679381325533730833709002539304255163521040 +1625855113537607234401449098110230513476322963277630854102799933050565678807805089239388422442620697922678611315082884292068865 +4371620713456528072846857825011777710835705836437187394614071662418465730864540826899869210260117229659041798505650538477261368 +8985576750846387981996728278040998786610281073838965731512714567426037576813755214228497693635971824751946973019990502558550741 +3401249486943615979298276104790818107736649764729969044884395409904447217117866856126816079991915988836153438356752147841144170 +4762938795985970542856369222277414531064648102624695688826552014393694646959714635482411149329964264665288374841117934547255987 +8266183745098026650707351627828760547702165330658530132998099760473617506446329738701905894912433667123567916471538563863907720 +1749733602645901685261363936495062217041689057898136406969925562689807608184144495204588063954048062251421026224871438707736424 +8548102168448428644279598842714118177587767821370906237000294038845928905112282335030976267725288843840126116450813362024012439 +5938499597145637343173003399875431713125443282016722261731991805628239317480073706051380874344456692406363674536084190692646142 +0730500561374089587897509875647211800809704761282604727960645054003482860298243507208373227297725664305984934399424216469588628 +8419154111597746163254112932894914530620901371133608712199806376981269451117389112518987662727134188058972066300128519445766440 +7937806580759161251998478953470159484262859343028238192145225348422179576900021837242945100592904945106701907680168893307572302 +7516873429370795478978284132360296301129718931677825172903818753611017830921544550227392257770205145640364461713731347634995836 +4812445494938381664839724186613091405433366166945041207304600303147241282114042034521353097954231266436563888803937496834589712 +3378087498752524611421562976032943128888258876939559646852575538636633313807640898785156463287303741217790696080003514700600687 +2916828602935343245489574829087284839505734067476926451104496434541582983914416281292812699388088495707067652976139900162078153 +1962925444708479245069456050692574860960690835458249514553536707157865617717301444683893731781098151767222498616081872995504896 +8173903682609218746993851247188524280631112843215665762250628484523471921122641923241385617676960635551184550463145418449684690 +2919933149036430836155770474842583951453613631150982113752681626101371527818957365564134323888669717562193735500885632774199608 +1609040194815030093545138643057865379099082109649230423000768558709693305039551156969735710643672277845208035827493067182565724 +6908049177553254273158603203912637795720376483171212869602699514186725461068506421013630496115516175113680573200157037543353536 +7096806396941038667344905110986317666939904573544009284947400783005195693194103701914010433669991415780015087815639572682791514 +3680756978168727352707707927350530897842094443052210887958488018513662258743146529661558086974070289076864455883890457256934114 +1990854389059420349848505548169668954938843880071111806085356892226974097003547436768393216697877346132002814225746150600875921 +4374492277982018508835930955357380285574652568017468028145914088210165891776161047538281175076369924948661559170128551211440651 +6393090046195615488795758266312386404911848462625030168594274415704463604586947949625250757933039912135660379167156550736214014 +5728243934436503311699358540925223297549609990942386098315769782770878982264993140803173130768644963918006938211060093506696811 +8242993502087208931436768299544970974899425557012952209431499521399155627662162396837380424393929443574272383673835625796387465 +2907417733054963303298412175669470571774232019901402324926626651661243360954715973085766036500273697051651414938093042950467378 +6785894337286818807967188916007632157799934091531302525882785328964971157321099749451607078727217297793573097237856806946698841 +2698558719450879861393803875387234736047009367170703129862250880475805397892793129358764218650002148641764709592993056227677164 +0081971305438865752091446464085352177755237063190907377734114566649793616356658589817576855323559323530686449621884731840150788 +1826939540243947080817186653825740075444935631469715645767626335326860305087307127848152820195439244125563274241365606473310540 +8086243883958240578268878611599696204485799969418845316458163879163274209827292559109804125366908746820981499563030997204003905 +0386391010049409963011357239704626669689746915426124197111331235480408669200583724344735150698143659880232480059746405028850204 +7070621356175994235487518719187218494861976025281218293612423462113181646903473241591007825352754630207984555163787848738511210 +5746214941035113394857005157649321493185603337720914119128667915231061424163637568324581504110784614852811094476768652380182856 +9112517888906528267503001049949573135127874375506725576149307826638851599758326023611417494647191891545342374452874064341534379 +2427813650814077661287436979575407398921688136831196702246080880472345267198382221158754713144173467705458354166291752011017130 +3427037443878854401716449891823089902160247151232344993329995686205840347612271617101573796125148445827775208799719717001561364 +6663271105359323644532079372626792834540392816461024173260479629489907065371009161907255452160867279588979514640773444750654047 +5973386387254971355387042682264494417370956942797373987206113528047687059080572493246240752693790196755839336183495531383520500 +0786113322119991260636829390212550532753074849695602064584008995135867708856455125847634381213436388672553141147016113632022246 +5315245540985412852477846866194175059739189599584743384794049685678013782286205098025299864897730658726559776885622145372207093 +2947046237055804495388214736381241294012375243250351412075115454784594240484997746146442841037234277389231586332048832314609602 +4940560326735767175083614737052886039378277408442894735731934794992054788139312451587128497131293085948245032106415584594528368 +0132772291233611554088929401128017045937329980274933241381990578374018596495898631586186020586965351932668997510872004538098971 +6387651070790112849045385204698059096706128798635300671169181792420546539133767532896269735055635232257735434930885609585584847 +9016459024097607724588041340562451108743288014737496316921528251742165151671257409207041532182399287641327587310586187223956817 +7534883738445463926481692970148340291005837757179973259685921796789142441810657257829230854362459214897426800925224271446152113 +0560193061957298132549489635518745231685759631661778699250394638322691452161479836232887573811183208096513244778566157714733211 +1547430295477490963534296984638628258877576273566145152920369666682577588376652374130208299869130148553971805471715526321961195 +6131510592032893543765491267410178707297913936975805122290905928404238596549066769665229727337467790389337961037867643489553699 +0609065177988688342181444376437189336384085173474299503743870199156371216381879438127542687917993261590322648797772549606051885 +5486486858037007564853644084702405129390388809213365108089374357918264647019430516503970264903210163964653753490047274772902330 +0461300174226405350596681300475321223682365336534045385167141718398138848125986935473489411898994178547406946367385725330213907 +1293509274626976153463652826393079169386713361776443893666230924263947932847701762953659799461100689690446626123374959253884277 +1306373580520533113454583490110255646387329350405309931889447247185635126485724261288069862689556565591921946727772549017471108 +4575946994632942954732825619796920704805541065043930109567542878956936210283544264617080903536339854736947121491562648913229061 +3705434044613436537344272909123399762720035093491390444488810776264889843570633561219106570438196072991495500597843887820183050 +9436774296203905562085230110417550251074216179370977223886873788396051869595539699599654175540263842606847704783570758848289347 +8739544541060559735321933455321086474549444477666793232318769795339438758735396350156993223591143789407202109979058296632555075 +4292457420547255026544250001229816943682894619632431862491477990869504856164569154248439119907994750443837979614731379296026483 +7759359950784829354681821925764141180511597239315053998175532245470389364554510124781233539932768390441633522487434841829277108 +6460068597420725351564334958788443977520626552046510044910725038151710003467025913052244964411427404827820741380296783360351862 +5211054158075000733081570753562084145801231533068994378093380973748015206056523195741701721944088905307131000364624646104412959 +5581089920926259625216459931700561828077094118449960988240945610479754976194870306805076133937666171790939426378434449418282506 +6039057527913534217575393813423847928955750038761332266774182283692275076570170104686335201401167177077211434292507428463611621 +0405115165537284758792010216853186661681734615099607405177733845109910013152569350731884598363743840624814198617754273927011515 +1407618016023682414983073935592618659194377480743081488662206405541839291934958759003119610099774589701968015068594030177949217 +0790315691986911016674475309797543936300728937674591920985541138152579574295860682375269717640514911824203818306809998011275060 +4382117983381412972994275528147432373242673418215434465602805780063289348278856395267652713597654578622689358031371373016193682 +4971203890127140128708179164505005602256484886747097346823610758765747601150832737555748482400760746795103492345268624660081193 +1238474101549902789927134668369786012288633703563425355984226013256465916402333622807942597885223175366854363298056733781480165 +2475056989181989715935840837103963570756393472493139600699839539995767516688497969127775142807367604482540314805881080534085920 +5110483916952128964226635567750120557932419254741576165448818621280817929369091427028028668168593266857526132988629197479499338 +7382097529770364245616319813419439630747996451787212372784898440040645194051307376865641947314145623284843530906118562837024282 +1180719306858134644273288620402473339088866088558646666749328622134176944113672979075244216767608666882416653166422367766359934 +7454692494850748528168077614552490356358640467830959916873691009192683520419187156489031520981784955116022176529954332656159900 +5703126531777606205672012335960434796458742284617167876316278828807209603472901804370309819783462306443611963348173237324352923 +7622935915352142387734818536448742013947936195902370636105913778439778154319304174619967238291501475263405722167671985219327033 +4192469391918128902822325360387297654953797790226858555687025028458729211895927349803665945095655786671449147679921097155626406 +2780677237649339975622761955036413080116980723465856549727438471457957358521359815258203894427475417964365478268462653223640340 +6384355742271287800278025268983527483432389226283229170112845354988683227778986191910815226499442938779725664362022274661446656 +8983529286956750153487719136198257278584599062333134754547317517831680020077715886321603644059583997509690986046227389994861108 +7329782796097322937140185361853575699691754834694652931632748528792770060065607908729085140248378226282625852546158156687855610 +7542858690282896923167331772353246169388345238738453225427851985834084721866947550098641275435407170332769801857389664211235898 +5799583185812386717849837795175430854770749931463501139883970148364355569591352178627597816164651268936590016366615235430971843 +1626691744466309451182517845050907803525893409042451069687831523577743242687080029262942742968372360368621788807914146915526880 +5318459023686131069631243003764992320489381131276662425282581511713606116289353391248019203404378565007089996278162834011328054 +2940914263304203321901619997487124198689066488749388375440671721678381773728920019056941032982814050260828373786697860844945937 +0935106263610485636127316359361586585062460943315331019818757835533567856247177900197592421873824025425985298148601575872003838 +7678845567273084139170475285839065925039600025616987691701666903861540800590513965950744386259900437461673353720707990879400851 +8954549187260534626790030686600472676242184422173770737899202674956746463128211733652450269429272431346452882295888511514249530 +2678743312277245350760231139022995628706226321476561623183303095250671287742041881664219630594695703026351221927214229218759706 +8754746770599874955073426285210045733061478934256278154824723752843570542480374148666553082213624470594178380618511944338556770 +1258342170448502309847524118053510267304683961421264512188294654208652383435713573419843151024833136714917076044792376946554552 +2686790894179727162588194653073820095034068415445717821765878768235572220948743946189649565838289653662296142578461681042115408 +4979975702994756010947392018214486397764251254586716632357033001934850295130508675410334819960903160411122706650159720668896818 +3588969475934951426055953320940144471459990302441248110905750552714828775127383961567862284258836730043491152173444309138622805 +0885835602018492226235865580484440117123283431801851414462459989105042310153202450298431895243159623304698553633960555968674076 +2495404637338396778354332975443524901010204103490577360377282407257576116606492214532864036869380580423420924036214250605437552 +0273823801820228766960129999040441972228765926389245168282516822705610151382079832776017382538534107323345050070717318572440468 +3719693296470114077607298610532514071278447231443437795910351532332738448270051379404482177889663554053263746637709700053641967 +9828951429348008059079973550469791268813640030847883313731919419849614743360559828672182938947231250356355114573949483277719010 +8160147790535337222916241165963603517851503563087426894556730214593005608351479878086372777584308199478067490440060949884647159 +2948684894394971841029364986618911826288588400559264365297543464936912860908849309130216792677280193265598685884468366012698447 +3139146768567604514882268160880542261286448423327913447026774946542138981930662613526814753066924945383435581070740645979592320 +2648770517875200683345474598491811627153093170689853703611806314466352414707945002443742160480066256003350127740349411901099140 +3308592816806695381210143529051488111319581220479904092647583948868369653515446635908476209492392955841254295107008775348098134 +9546057832057661112548846778480873064251831646186943320977718022174333455691510572548784701482794043711785251965805930805124302 +9797598220825240487337675580868560618123083511865310119111306172185132317065154479238514833175902325985968356007574306322665172 +6169700545459665615556940263207457415925074513750097043236324790451429771342105725243301487094857682554118664272164353388631072 +2065773265591123476370674843120027879502951065822173174917589895449890375502164860452910893685397468281488262706240927914630538 +1707471421521563472417257512298478800552951214730430454446551411959701879859928077389843110236600965268158074916952323108191254 +2504020366275453319690764881376901720307904782764373433647976640867427413961893744590272111954111461916745192557787193817126040 +1588579151481338108756894573873934357158704669830936720767495898488860812251579113152660788061419535504615986627104261122375990 +7331623622011754370645778091243569467777379728540842719407347993337611435365175147413009114962875879124181378102542276566594108 +7484609326756980072187285780841286543812762762534587441056462410575222582806689121646585229111979073490526305371963936438467919 +5706586317647554978296224372749817614542161459685489294484897802898801747536653246958762657311576344225567199024088879682797895 +7982416710167868709570332702438980645861985176457876981357976675349506324625350574864591977896798566893422693217353865223434242 +4612810517637816971256206727690640808547458195070320671241318078156152911173576755031013652809873321281227880993755057868636257 +5869566853914426396407700744200965755799089377206196772845889331869496057116760154715122075783787728423594874902542137832225559 +9589225721722482637458883619679136109478999500918895037360018469933679740571642521146059558547268528193088694289457407297389417 +8114608149302113225536168603740452239048816211285413122470267658232185443153634579198429078537401989102386028340268733889611123 +1658771744647637356624539664960206812688702241127922503053926273465214516451739790546967335354319444825617690989800359892589856 +0053465651697302095039711511050038742810220818349852842019182724663703883278427036626489847521335203092583455303768771456368837 +9461783935995459315552854731516681231226811974225458662840484653357703794689886592991537310946084831023202899386375997693481512 +4533585921481186000403378289480817856084298244494770175994273545179036968044114231324393781909984894250047249639320078105176941 +0088337423065130496692628858394850230047403024636567775158827661121094757863694577077324592397639708861018849419374525563390572 +9433907825695888321692655143225283986904641901823924591220365135713855259383744336203797993519930945597887702113356326830645826 +6988446116797840398450853279454162958768243775646820798515687585868046231740714350466068297160555230698894793181661331483900976 +3228255598385373641636782196372646263654896275851591790742732186929071139789500558984105284755563028281035328678092579289714000 +4681011094010579149015425103883784622081638214345888273901960768244903621448040859732301956914911242855175944393888342553563087 +2560980412018398873410432462155957943116348971819529996430919042949095207925589612393187400930103605496273814170584912659092382 +9069469616610979589154787060025562176784632650316733720274832314981371020706161084351225782272365506817110974959084699634535788 +6765587755549143676480283034752182178980320308874937062805295265849032617917049285075020134633510431916668093086266525800515736 +1987129393090049213521579473208927794007157494973792406906256045288699283419276005662236119067885571670938877198469481410429981 +3722619269607651225319072582934109199126677644296125933160645018724047938190291886105626593185859522559253131444513872231301882 +3878903450400959596968323921819162239731394330071230682797573061816799122019281619186466630116704421984435128057686311537024574 +2171772674581440679441147914828465322671796713303729214917204516452594596558898159147461291026535836576291138961274078954494387 +1278996875658664588862768319875101010284199226028473900312140448349990579106956212227946883352757991736474264479730037979113425 +8251737524782800721142346398693390112514424073577724343296177123905498209033583895128953063498814237667366732979745537429555278 +0896743149526940034664390783553213882383502205749882093466354195761918368893140199641725113693740413564898871253806941096124190 +7796854830589872123486600630498401268330698814798157768931103798808711643064916232366065296644778046047073412187780619253278677 +6500537503531609708551372800808683958268146390074367413801774325497534682441389495398023074938743544207866592122064463211672009 +8374007659269318743410884141200642883663626493756879447578129358932170292700541285549708569643545027306452528039696051958912164 +5461313220523803049639223578843432913631643532704665479011316367050573380692644563882130994146341345324086855525040200238888685 +8089200828994642111859047971321583377842360402699515255215765930754030446170133309029861283548123601950771958367816928039501979 +4921861981592932540366839797291842409375656173168252866766746284441002285941098783812692417408647250610811303546299712956961745 +9167799975366484899208629064327399424255199740686280222254809555146344897012896217389558240697588684347940576269967072665683864 +9259388885200851860744997606520800745929512006692275733677005833780389985037437438896650034386964970221299725643606029624930990 +1923555984126235140744073661251739569863134027553494228555366490941034788475845541690058986834251129830243447779754071605873640 +8546321296959618681680784347875855095908171158196021873844044080735403609307093046577378976184646301996038465275795577308210631 +0292841602799520083981301964025702228115813520124006499666068950846030422607104364257191751077960220252620197225981403964787782 +1502322992487049171872430148257934075524270686852895414785698734102981748355080650539194240735935759095611779799454232906920170 +7645673745116807956634247517521534891766878215315481669195796109233157590171328181080639943010458633508897705148841450600988472 +5372622766288568973430030404326811200585106334044292931194542876927622884286129265334479806408440904927200146880239590676743160 +9737852886366125426992930022076166695307759649277919719824038921587655496096287480936269368326605698820270793854355939980396762 +3180790631743853089037422811844405317287386545411386410379668362987107109358079426452748318127402761332147799830440163526016129 +9051792458167155549317710812956107571797762247756516089487421498534083002847984096816239076561386593955153876706013822381927681 +4173095560231171732875361485159122658902814416236442472154640300288401422165407659115651941434514022078384640403200843998671972 +5966470556331046673745430553457883526280940892044422560744252802816860579980940340917982940500224988618235941234788610789509168 +0789375239650015659257828854233372959702664651151444414363942452653680312005125741160555543069598467797693116222658921071003474 +8906623899466461239060702070337224569185523906567051838309830757794131815206179504935357263945463042333252081048931892858013514 +2565946185596833054040544913117114680329109501977878093743966290348734278988799428312347727405020457211698124070822545220367599 +7887639027440626642608258462581197685627001806341140783071773706310063459682140433859377089948217599328491885205500920242752765 +9105042785087589492163764806502467978793005126850254340679666786999523605304678430709511644540488408967072195820451312364898977 +4806602335002501148210169758225301946374240051070758860058332659173580299590222924023022260764063882929324416573334085874519685 +8860484115578005776711551777005731510087855975894724843956874006078066036507250577776451260714027733005242056180700853839120022 +9972510062716845450929888769682540876602068670550887529330503339523242901354677312818632650357341649500497585776441604969459550 +2623606137079639076047135393717143616094073824102105929404170935516181957666178130675618698894069752882863773580174806425770058 +4036671892720176871088682314351824213287902597131301780764439176800136663620269901795969751693427552587437595932156820043424392 +0677294124292240149296066696637747776445858679559483174535961774585189431151122220911414705647847144029722579499169874681984001 +3515611659295600392682520429928899147250410034409210806506732173247308027181439461299331344368884698174936563300497600474483708 +0063773420552619484426050490136425686777123343387056930411442496315945289353053161802745807953399447036489317450479861853401831 +1025437802898670603956439520711493780674154126180902484921242469929281393551385642769157018539956452797615106532069322627218483 +8759796578568398063968102679425317647163292357375418469803494058822777510870684874739268000355169575539187937262850844553141671 +0520130761108166376660600783005143635657232142524978468353606381401953088627097253750920455395288123559132491257402067683088919 +2890887289996966535342242107315365601206035014159576380219737883962373302977227719789793402712556663655879816814402801231940391 +7075049290134330330911964129274745947329781151686133509827045508476840011833420977165532815261601339404747681713954176358695787 +4190862513692697255977164180664759574127822507052658315853043337010650269787647027656260221445450941719360532423677717036461514 +6908155746961875390092893184110336543104604423996158420042649680420697682015958655453176718660496277341803127921088518280061343 +8331309328194407889735199978235437818800306669005429636917360222653217191676841870581785131998034245906906399415366961802496511 +4025301862689940996141896458524993405457637230423945068131717803505952316603722440206101338781745994056920279938566008667957974 +8407156560172061727221211500538117499687747789630891734019549331980906693172176957439813668311313041124185359330130579817813671 +7466416959132373028000515440426004927338552129675365950832267054293724597329240325990928091976976797329108528262309484406803795 +7029882344252063506996455416881601769204928489785629548747090770306190718064516950313796925310576174715934481881905681054628070 +6880642279419324327941511209000580929409891074059158709439715032933825582097408872913695012908119133558817540571147731630417054 +0450976304961807996926719984418185237945644387146277128118939816965978556618267349869880710186417738649005061794411263100794703 +7430304052534898545934660709004803179571319216041424124846029027406133093839979267839656729246840664461817790155804024698154985 +7645544772776077953527747512204638297554270388027111651502874795299134795410937797070656228161921719694817879801901345933313665 +1313410076049663998827811921356048583498283359569530324863158193199961087225704771561935232487625934010979224924950123845221938 +5542450907649094312430747916423053695607417672358828335758664117685424978394617325307248658166202146920547925646776725087468720 +8125710483678181819120321493187443931435742819361342552711290450551262872707443639793745995943576289709507020621251828473114770 +5808995455056792083922234742431742082106369821885564948694790039533752228468465967393391425326441602867483086230714643155046641 +2407915004627569028579526596926110527896052886334582385573230907225818330304996992697006735418150830862621130034997989250568103 +4747836691209220024929494385208567863244371223110054782899030754719240501536232821233744154670656379727685527957774155672319475 +9632183735133215405359145903218054467186662532650851575633645907886287911612022173510068469813052313582577723168904869118582615 +1758443894147315526858202229285381300192667883978558926059924490571584269631902778078169815289434853467255533171050775547788250 +6827379725903576619807972215479471705793002981926808670091729392075163455575073588508765374074600361763199064144608650998814612 +1139348883195112842120618191199370895634072657781661691958053236883789482321308598352679539450892103726749423679416567117182472 +5628380449503846780627597266237186381426941668768981275727365655268389180764025467621494196092237996217033830813378898293373834 +6249982118767143075646051931844250281862161753132641089322013640269043322260625061753846454820580624883961230554153956989845603 +1506389246361534372134008198497771836659467625678409854914006570144648661265229365915719554541771143877999578976720337073985818 +7005263179523699177762675340194056182817188450058666203568903779623473238455975919469726862072009578002483873154457672262756142 +2974648493466373909300392298493641388947991203930909247567333382267233769728042131150238686456747730553128288781731025800074389 +5103801198075821222369482103926218823537252278659224466101143285542928579893855240568803378461857010249847636234366813910593321 +8255444368358914408018189587539831385866661807993390145935560791507824844153502737089683973595182396850151415917641412882278385 +6994623624086457510788033568938511176649748143257081559534249807952079088004873263643117220080770572410226081636499222544341556 +5868861827549149829709753995576140261303291609060728919357591890769431437067208298630183272025511291993873213366101172628545169 +3536668590020988868465523872067378130539719812107645831862475320339405404292929655894962610265222780223832442563481658864747632 +3855559579815655284020206856324714890689773172461142431850331038157366681406979208119201701019291754639995462477549126425391350 +8450291792039621077539436402960450989801386738795258875543765926494042577259109968714035988972694939518009619956412673656801521 +9433075795122748111293126771649210779744396506362417079033687912993766618349565561906099136133480366162159291355560623721579187 +3378797875509332216281558051343681937584540368139917115626876588840206579792879445323063696609803160334574023091211019696117960 +8286659856511129135392256673984667705469454940089344874655311260297659378259031305125448870028720171028350742267966605006358033 +1559883131888908511800248016547342952257309383028102249271256078844439208862517563884780187208616611438790446776997799461384713 +9213000700276768725565441947730287329227888200262127732583001595805481558214719061412862292967561082950848644790833398460564723 +1237463356367627789252505343790157426584837476951882453112990120548575795054118752744721410491509448371890573822392145256573546 +0705282244277315586839396249472907788988824858086668101214804950902161992883203183045693099700874860748969961552066568180233875 +3649804362910298143091615106831740058421008356128934335979284102172120855125136327683861035088765145754792315464262726624806783 +9434730144784579817482674138286237679514020123482574946846405367389289759453442305761634017866345369865209680919734485563877769 +7735810389258589718835809442124950108708303704945904199371845065484839372522859566124912994258804441548671551838820818437896075 +7383779025406555287701082383561184897642996999173556341153509465466385364878232088945280693653997350128318569872380406482923450 +0873625836034957145587001580511059464110231294763738687292419746897783167632026738408026265866915946594910670670203196220111099 +7591072259741426420367322902499121885937012842290323842004604562106110626665272233088406003591488022718144482545987747891823870 +9175117307974364573779518417573849521480439605413624062061689756346227400155445091604379572899069013192269044493766848399848392 +5240204070345240378009455038862456326732346785249576960346569277468135720674493354585769115025511723751059422380004118585409709 +3349173998694448270286501817135410477702569648480525937208264068896413487832339295364105296137401893118290448960552591536754124 +2721855377073042117183109540215181252714104113649070283807754222580162122302324470826848007123339852921541117150545915474905734 +4442237467667117475961916917266329385273400917266015718314637687934575915917583933649989627896013912399205012102463085560527584 +4091026285900629392132231811067909337191049861336953793421590084466340471776345574531068461460044327698926685026108185944785273 +5202568174541673346987530914965105932805689677456919213224331168071054852507039901870720345655716025476685968559834858758422493 +0614858796890361150654138608860068135509273139791153876597516417474596470901950497034522305572817639212080825359999795816120790 +4690019462662785935463711750437540580321430169890806355137538513325878535253326703788253684313085653375194280379587086774403636 +1192297531414960747356858854539457018921020998476941206542822005080405209643807598603349026957600933047420780702135587594220420 +6385879114957936148095665992720513226197859511089355764654052901972661068207137358088876405026746758171573905075146612191621724 +7225256074560068850195651541217829216290435321680622077492940113765770295891334808874940394348277026794361425754981697514840551 +6579386119350941004072171940171520374772062901476128261790580552298150743389764668933362002827319670377285437400224631386514541 +8225060930462866114265671430768226751533399557663700459188510439035058277166158167696828360029527807376628848000272330091576713 +0060985472860653127812993949032906458731116791182858234879179245998734711417931089452148679024170798661176089885161196837664106 +3722805008939449259124307009136610112592018063781886320098372656598331954054355261215122816645348365251690431718100984807641151 +7671563753475866652087409998956658850991352510305691362750704516857124752942784347614592502287785972007339688267613862027066735 +1058283061155393934892259910225706182663785747481861117691921311446649045132526185682558542054377226003665649454406375422089246 +9732227956822666337303879201449758438032098745926653063569926833474234804332766368153105341066282800778041496886005184241210550 +3671076559003594556531982936604847786145732585941552091406028722895252734130685947197552543811727116647815405252804851390263345 +6932108260738047486627928786730592183240725916073436786293871274003576099287405402166418657664483313714856186223383473022466647 +3109816045417430083537878210451471667742535838802468841651352736464574926057073048472886864367560530408263557356129708697457730 +5131047990674797494394562345612521219564146779615283892037401029449518064490250135341621616256327661631926681344727899774392825 +9793560539429965156147946048811944830887554283992612919784433839525971829092056423046384752108153956186882584119526879128063725 +4848894438740997315189560856906795393017681147097928684567412396811955257068230623390617279746246419592475583485164533349133972 +9247196347885177565475533963701634576057917406510305607776085697222055528129649096213092034549229625900854393776713393006598014 +0895574299458423626122169092017122327940337399418681543962293182652716019527292869659349372977261887962838657423721599638764034 +8069818312801265892359056947805299813404068292205524125399036272188934298155723014962611157660369164361085013693243392717540968 +1746313469892957608005716135870516847580996237870651257913684437466577959076954626426758903637186057505535214913540381448786350 +0826130380070802481888865468100577235559992056039751752075537240976306665243883422413804381659448597324700429367218879041686316 +8783969310605138136810926575209117557352903553802462523920408350875226887462880478648547408932195087998226093346126517623953622 +3655963588132204550439064715232312197339282921758181601943083078440247650220106399066615678880555199248431536315260442825292972 +2898418712339146920156618115849473564647025888554958680753487875973131958912468912448106432964749351366422577093050105438712807 +1852498129286540733225557917645869957332090278845330056842464816843083325044360873406582227952116884337704935341689726805650922 +3976284069253193914079828201683985526108157492968673193265258027640406409120387997580216531505549000204500196895696438933985023 +7344227386409800578949975082890327124630045606308364332447462178940410322375677488036092734345158152279018751575711953710675611 +1365860719506143953539467521806358327791893587420884033512557356449628421974197081525171097681451739962654458105945042338824044 +7339951041651291336434788317295707347631976285149811926440821662705811682404836418364754712250487175296474328282212659994274238 +0212161973979980092408896174977830808566631947896643996545492647236993898876722578200269918604906863319277003087190525085408577 +4872695328272197013637039406318385585335721591609046713028487734545081008963689929406578869624586103965589484238888590831714070 +3093781382852235095749474972285903266360882836791963595009076006750220113661602476802687021999748113402446232401462878936110934 +6435034693320558902105688910308538965196623309949466683973971294957981775446228096356658904951613108961843850602970532678592796 +9900412913465232057825708257994535036127184304885007026505543104350741668197223958911527343764741682299736861152999772201279599 +0514985184283479308437214277006973884479296109534451984897036545588323471166529442587769709637302971344236763633165248653048272 +2548677505362732783025625571327094753804197898599649990120724885114160782630110803234921618274764179324553264262464188071111602 +1490102219572319216164107062405778780633273426833137366869390517112371874174117264586848123441331500645236212846316718716883477 +6374557134532066647778099833443353940716636987739316642710720220881882944339283639205849477871629369917099561362221287484469397 +5537933700604880504714026554029166587597577826204845542181354963551493735767539318268321221914383282788245529093887530478824907 +4539736948571324006868573970323898689460869616752573795437663232396854974783326373421982301397494963660135114656842200560632231 +5417628879269942844019831356484605118567348015685889718110291011794547506306795984565230200245047127434730978214554859175571386 +5244295129306313578981901753289283565322938724988065200258490956019375699629296714989839545066652554067148655850643910689520282 +2971836240187409511885225271973597558472371014435493899110877817281998758924278138897958733963027723716372919312791817550414257 +3718036377547310037822620295119160273747183347935451508155041258644862468137971759991204189248362267356043409939280563151645203 +2059403653814048643066476915696010105560937121353588493219472721117585709084500577027269061641086537899045745026346755806963182 +4321247098580397470005864122314116779988792921164422171095663365610591552259489098505982208555327107941284015766148898178511334 +4129075760977557106793231123557041651779523250483585239932004312719534628408382643294636229121610127684366632908893383504173184 +2529072874533404417364878145806482135038499775952105421793815591456627903830117948809720387389717200574623969207051170720940841 +8319746694698707336604137199806790228573013581807699678418425604568242103303802544153333753105945256725487631110225791990763711 +3880160358558853965367146276153313624158725831047412244380280879089394407413109649993209520904697096729360273647396579418687376 +2091906829462709596208568404663223009844823381264480008433097662223468678702122339891481765380241818468489069582210313140199504 +7634167069455911451763369770809802453049016134384625283172635485682336911180471943157282346432504400241673963332048163371168819 +6945528084709716553738011980160563280642542751055915815221655609127228922438332498633425574184424076324567457335614537707491118 +6855985221224698806898182154405013337026345633363958698774698699505064139860586968819626692305575161911369355200549282693239574 +9661995161189339321012455875057211575581587182476156883528607442238967236926672903199283225630412167629967841892682077069456544 +5556977126081270383421853880021926196747677623279360503448724398127284807443362608046943892392030345708092629807650879718315338 +2008556770184273576766805643194174770708191017610672185545130733621276522353464775534219302741371801405182864900478774809576537 +3865082900785088869003731961311138273432940384506242628178208362272250891137755623037615929981020628600026015312604846649871045 +1810271541153370013506194385701474067688519311563701121092950023460871685900667973434789085894353689769155959515852286976707125 +9220855281244525680311255117572398240118776083069705246489217127641378521494501271090294888487837045515780398860147999941580644 +7341485956164711767365322167960284966203049807458284565556017984048646824943506319395879136232290301571995491168074801397213067 +5371405547126368328180403806382396944697802094247261778722922120527093199887586224288144259646007747572032786762234373440343768 +9641998676406552363362334096338343938226513712459780071504106769464358782102176567281764333076640338559714360625098972864983029 +5780224793125175832096440772460219675603258721147649221639684097269727175225573293185628479999177881598175668238919939024400140 +5871315749737173437146622211284749783435729572078139529891438562616324259909214610231102730312223921245301659630109254504380334 +6697637358599635782137313649853967748604644159075078379860391872979880640358744386814475406235163749464766557762029498100317344 +9260432917913444420430985635427292687992391436924828756222785655924726309271829922076362876173530631675597755661167920958784469 +8008272163146127952220122593079694523591964786341879588418492633364085713190449203278430539536800444410958318893570820694596913 +5865771428552651226808077349460469511350507824410126508014924137043903316541107301501243645730254790431504727160432598689746741 +0797866504326277547835398517451743711623314102885283925539211029404096358855485908679412360891989483761400122402649553985042303 +6315338269917337190802305575034003552890761126872174385267928282692687307003769845400327640284655468895977857113820663582843212 +5883143761391732179613778683464000760776952507578651405618216504779945736760856433526347237980076786027307666785673805176780505 +2608019170724255979187609288672429113478903050968752935506997075853793110328285209939065370268834945095553185289660445134585271 +5934178091636548622002305410563118241322636012045119794891759960583808782939464117247149044590174301040963274743429124451974081 +9822212841254646950102433569804383682318140936706127266382165162763534213426673482380144302560262856995513292329891211427952322 +4939074943058922639133209628604240951813644498408022254626546671648111368884617471264005460319454857408802054462873323814596304 +3567851550694955955743156890344607975932611999373517821856035756405925438219730313496771878294219292907299376903706456079314724 +4355891716120089571168867921156570013309057339164893294310027619061553640293850173132141828093723591781265620877693908024633891 +3432150765823697928455051347308237232788126878764759364421680111689906395962403729413891780224106047993476140159348719814489238 +0234684991575714928121012708547654894568292786765465546202194583822334911755447378931758555122045328265456840620648627956740582 +6213762564459648735386518381734716590458385722741140951208497160837103126054522292981022939593107580769282971517719856277138950 +9795430658880195944561326976463238641719113418421451290008508454179670774519228875316092692131997401436104108663905564902246214 +9652373982485787988119955149332007099514930250170109206849474570844779657182243727422415156736246473756296181666403081597210612 +6363221919442807657179269261813196315614323943076172547080772762082497876523976444410926957078802241443845971487934887307740781 +8442538716705496485993660458983193034639571775952652456130115624584796446966906201307091498198760859970990751619599499849168922 +7540755071266549940783200519568012630337075342163311989445430751720634069245959298519839175642543857453232073106471321794307960 +1699245059009156264426959609151370644710641713396754020166970217700932248280718651485571836633670709702375632170998942628345680 +0816396190588432801059693946646313280382672711087244012208759747817406868034578850546787526313820913242493648577159472787123381 +0759322746357530465987827894614798343895157467747058744481984104257507440485290934854164189380366013210043776529824930881371048 +6385658080000822018084506735695449199723297410443658699992360702264595362859622022028092450523165950738306668328519968101921178 +9290969633149036611961423129438616262578501454017560337962795366936304454531175225394994520495848281893733418969823834849646325 +4866905011511861701376543806753349151579844197278285044705460570867283644779510401658626041128258372474841328027560368075591684 +4097340967374048459759351247293757377219970623863320131555594933368226711740987383236414710324945367020743532195606500683770435 +8777289231702354648686196142357855572861820397757772835817870476148855333166772599103631182225390962911857675993162562793056451 +1643701292272316612682496492137369576459996136135417810378930005125534965897227946840522877611416279622753205256995171831362038 +0366738891350724663780781742018384952618867530073606447030185424566647732433327301660674858875240792034393577096349757765316310 +8289591408206074373434407333542355057011414872348953466323958988208406535831896578204946085981588003478321397316316325936083627 +9704718565559121039339280928457498417206123777131839564784077774260566019404908403357317188872113841441577298919813210986548633 +9293886303171570837540716535349361584600602170148405662530496205079833641395026663688854207586823003823193883976384903470625532 +1727675041270249998973772127828169098733155135266069947499638944765750528948036161984830730668217006998355034769138526948886812 +0092789759212121413625842309085282315688319684507180858726957280563112424218015623004324904038907228951254860936129174811193610 +7248502251808372373101361237612617365642190393287142805878594705588056326330546884029678052177636399750149538893582015648656691 +3367150919108114535725714981888781359653934702237037159199343298241562681613327166427987327289684634196866794561168570101129576 +3376393226054192039386172081865991731275655717454440752850294664224843539838937920883445641690602345087514795742186569107517414 +3623971382393048718491241619316545941650237621831224965329865224908967972818626887372855949653336237529478398264567160685068577 +4052813893095863733367924664141451148648087387776942749014403408537141959668060633169208644494300366994545481397333067992909358 +0667669402093903159518564734675653958529086910570255872008713355774046040785518290333034404789497402572158389475386895486868259 +7468963766629011848413788837165962853506507117471181320045844652746289086413801588758664510368868946415159144280007188633881726 +7244685543653493127669292847190750279258471265994953355225441242455255911176450067019362851931161106818923851255323971731656588 +5234890473800747514454607303289634698091529945347698407177688996229422965619449293992367330399443066079751703944482713291326343 +6459044264277571164204689989304404957960666476059760392612837445978755062781098058819661144475485054149528425812865761888665230 +4335062014272927898970122302854052906295227383275313276798239957308395793035503825814636802802824167294443473540831595897555019 +7272548964073946058176039524239703608785612474858328053422538963447385787828187985994849316214926394617015487472147004996421715 +5996250931763812814551030631847429127490485716717613966608339966170653265360723312151608339298980087240888475983661731432872181 +2297536852364764322872184823063403907099469015512172454623275616030363118664676670421610327708607480232788374190290021972622793 +6341746181410744070773088966325798253704849845010112635545932402181743821794042899160348845322610793015131752714444557452077603 +0974929188283489944704823132680006629007254931129889666199080416689925517182595848963795936091170595822025233402048475030835352 +6345563891720858751435127759306670956633219054037703933137927110019397801896389210768937954353107771885376564624836527498565777 +7406605189056950050564580620487220225704555865872099535962165458171814654057503675391062376917680202395298499820248250031013118 +9646002186451042668001138188414741520592517338749162144757726571293411846579111672780359450504540363461450479439757376293399965 +5620887367929571798109873216776069586763980872092222280624201174614361433514239488022226011723867237579885237366697226018636145 +7658816676965243906096876039909445364177324988679848256414790985301032302828151276736018655718069421805321528401722943260148087 +3504531887253350368429518151187892137924735559724530027941664978322897818820489478004391185810494126054267658700325358193119630 +6476950573687760010199531144942136546086284325150545828401223666989902218958065517474495615521528611225439578736181496123291235 +8141273699960775743878136458997315091877968088833966155435545803714212437024032432947661397098373986850161208575249088483734667 +7526564869781879172205819920641629162261070844597301726270751056013595355206013946667713217060767795442616244184317275433955181 +6807778755059399042252363801724898729347456888637350665052001794960468288523356956423878794195125458046129085019792922364372832 +9606605635909164393346308754559022246397895156754911940817409436883533684563312856180585298884265000503280045621922703129043223 +1353282688124152832117904166784884443448057703427122162928095824570260033806131301099883014390489674198929314751517488130229287 +3941712694041861559776002423763440625536805158907454771393762216105225325673929237051966211204216493810015246774275044495594616 +3694132772158289887311123798026659094603586436166517150338257882694213224057803554027693325965893134619716724281180373864668897 +0965421930380580362871564338408316626579345707567981028006568747619015807088576948951469852219385041334895298633260796214042764 +4365061209195177145333592267894941789748549729388820653503884365772532158665492262305741107905299439181875779535940222413616201 +7002692668339856374514202085788525299569973429859432424389034479629705876996023622182434169328690891870930429505413389490533136 +4295933444463189433951574721728951160043627069745406122977006157620687110134601604174649175489421916452229565760756130297102064 +1960368933597422050626608328849588309269037647433491188814133197810733941601294848194060829957660144812059760171213031036827489 +5163673581923411040228773859014781426438397455478270422462230943695383387244412044104565525899545629313341781559828594738866880 +9340062964014870509227099698887936675774112546104074153547361150335199188541340855227153532746778775047019132401967059701871242 +3486224254115155530038741441994666898680466167832566881940775772365423817200581937154559007068187829334117784945473428671685916 +6007443619236946702946484250753989318115938165448366527641686620981129998037010871500824063178061129082786722904909804168031283 +5077049104333703032159256335893142225775472800145797927383645074150506549583979834771372332786212436563421519736302401896553214 +9328253090460566002578048864828230612968529907510194421722878024618561333479693530779323192843841998003032594026649617397117033 +6902940885468724446328665796554679901999854957523032465138455983570952184108442966984143957137952183112915545991326476857170678 +1147940566264959316051371696041947904637481710293264721589819021588178200143221071704512368347198312678908673679523259812561827 +1664627873674389755627490418493705966611982979380988314739579220409030725477944088566536789756067951060713524593551031845158984 +3598081566704365022254984411009080907751257030857437625615151390400729972784657938585703465777027605245686537246903748184890274 +3743523412032108137414706768919475408652123860125336712812291505493576913938367683595349130153523703056997225766687176144402340 +0831940301479210349558229457276992358900461281585291044594812536347699257011182959431429011534075284989504481274394019519138849 +5294182590907051056305209676408046536522421503936187377406071503791132907536141109384530248717758304915645762984125835020638228 +2925263782209751558565689180869492270244285946019041108330056058905348740666223484108200048676063426609063511425234359377856803 +5066811579460770402822516077692569897555102184861090248580603480179709451447581598869819018321983813091599472628576647298283510 +6074763977940235151776420120127969582510026900568916108091774893522283047922950769687105424389690185426520673414305707753060144 +1829809239775426957360751626081187328513913192137296926783947378382044283754109795780048763911203026361961183033933012273700578 +6256148707335785289325167505290076615535370263567053348838880542975561767345020465640090418173271179915907484593965695502745441 +2416400711412329501943051474827325196149637971289654356289167838113336024091716837472756809029018726324544210527893716549434408 +2077670900805210878733285491095692143052856294682669529394939361462198474814099442765549871744436963471806447226559851872954947 +9082929602325709346337243734095265641562540772131425161193766826203999287761238275537747088073534676484013517045928815906520421 +5721840157618684926859754448303249283214882375060250302441861554867804071849460117284782240184817225436567120551507130457965054 +3640323862784940017360312940091490694469989530945043763136591022334344797040629558445856236949010602808244365211211804494530091 +6701192226260174575156745632984043284760428280836716963199179332091776863302070789290228857289693019520852953529236479717683160 +7117865720410815041230240613271283107726446255341876904224653305719269008887983911844926604802435682901331270611392890319564523 +6692783449614566649026808017316150504276465383033625466313163064737973819404507236614011588134611220037079763294452798973884759 +0914820111491787540225084826895277018554164459289529667607470230351077932208310255555375301186238522549530796497320559115264320 +5836662613656654780353360816866098001238641884282152191319830259179620144977780872729828947449487560906147944637380292807461055 +6561572680280173994959056296932010485999239763428796091258639640337313710982242786240160248574519338042692530967139936885572234 +2825509566854748807330632140109077953768464116764873006974740017020211637781546586566316927017884251448954820201170637677959988 +0832497323156260043765386414739221132824464852853436205409279663532624738981652928962852787063322728977472723705113895939816648 +3887836720640005567311856937351200913471468555895780989932698604802967755682558413334239668009044766569135994431992317817193615 +9488541867758126379374062288207207762501971013768400508156207999323493728384215408284344930017408986904527951285518045057193770 +1193456086093196883061110691053041347131507623254475732487481683124957212918787096719880296652189465479161772651216902388161935 +9023602670135872848924155417498176225830627494225163432154996996459879170929691453342113731711303819248475466023815067381174572 +2080493294756115922317931111866355542145263306791051695700043555158422272380979294286809277064163627702612725392431921737967789 +5506584820111354803017904304944193794950330337626863953844116710622843699200721933361001889997152111163384731679702997569228667 +4453953407265344555661480951607569587119336749659238396970668832430048807500071385571032957421002113212240607304287845329689873 +9400568404300352678168177542306869394633571699501805048687025644577243527012594817839631515308187464636399822139186673563109194 +0383996386779379889997394025888104236940479474160100383602505977089098334042031033748699121508022116963448387393566265472288965 +0847531693445140037287515174964594859379085364324835476807557964238229721804605800683718605401718393447954937423707976551995646 +8901279644569224520056558762822223149315014455057454435980383798016264269841766056870811144787284148299586773970705052525539895 +0256120889703056267161520334600912740468642740690884187820554569479971860126461193414499951106260376564440623032634236822171234 +9700910582776001785061958742334821043319568168457980526939228569330951842352229610659307009566518307187068255993195333178816924 +8084009439471986482548298108494644279210299285939700414080731141355145274821687079465639330167799278714667429384500519830074150 +7502762255088959326392982513450122047410180131292434743496089993750128169949631240918611287380175422097126458912263961350683605 +9825024574318602570230904976205086057242856859840660113304499069379218541308171998260227484149937812807157987838492362751492073 +5236013621279011602439272005713397632062733928777510296548649421999353087528124960783236699168282936749555076309059950538448044 +6316174264399599155501461815935271957641181470379962431535756895151732821144407472754664303419949275610161730902538833181412620 +4057262138394989003315257406621046570918156102356055537411042166653273066886196141130782850554134831168704085162214611295749827 +1728494807516097505065593608124398864640297391491113562997206711322054216710834884111534067711304959653237337706486015754839628 +5402941401919360128506367871554679221289967323748328989926516006333132440966780154068277893259475878724270654107817685397502294 +5331749302510962305291613004333202326722920832094875206002590142911609625146433305314091631233538797653208605364527924132244946 +6532771118816432093996083328883576769019322364788699903259151365484388584381218032915424365355589731770063912720956702509258388 +3882789530639893098899335537330679624810372563783381211488557543067129370526059579061942074775015254040779746533167078485769900 +3805020437690986060459807837031266916198079638277454221416718947378635541609908151334933790442634344916435987118766221380030868 +1492873283652410067454455812350782971829174602968575413579966330593167559586103530053302924740897504468176712345572924410751989 +1402681422012394852214397745855323854981769611609240399129690457712558487768725787104039715649959552365787249656873757201153712 +7034615208236270834171130441362610841044504351389336191409989539270321328989800821619371048375553928712514470949748975085093770 +4290878195796201049040671016672715493368576254144716365945552067883559930088219118750514982822693100430367769784775351909372687 +4911033872681823931983855759969195568792322301570737362071175550474999508803728901587248248809147697357896852570255543589500900 +1706971004869558605860088419347382200967993695904618287963224847080176675951145483137675636512072408162582506993137174472960694 +5573663486380813348973168761749599259962367833751791115712074445847171992391927789752756447549332777671869328971664763925290830 +3549115232652328982096333522858191070453865857146234126898896140406643330756492575988127393599824952457257137615690188707592890 +1525981890012719417710592274397314151142262744434946961995671692595224961001445969622314797497698000523423663899015096913051877 +5058249696800337974138653960205886721218275906496709288754537792787671454609339328245451684579097878838116584542322616579053187 +8462720851869420776547815997374420654356793047547706068426425982466357700923014078111329124212863212450480733486009748682342594 +8687099289779458763026259506520765785275805315714163404563447144528065795533004752634001703743154416316364697795177031670571395 +7862389886797621228571167458335190181377137309906389411269247283889070732381228453233824760431622453252798014077600435024553202 +9884611862481302466807813924732912660462964877829202613446787892509006450191623119538981968974508042576824347741032177030382425 +3275371888164672951716608075300420614160898739974183834737152799222716640347298255880801705875381815950138367820787575063612645 +9756093031018363423853254508819810837905492342390007409090686184553436757595124339753724654015162734521998021981012802729263275 +3427616385168365599989752741868814273183927471411894804297652680678085002922251517971806076560284843234235639397961184180625624 +1126099034992853577542894567722591646934072315106827450408752081909824273574814734547622072209980280969251694858199479989346866 +8324744163428589301098203537736106384602289116499145216551769186060030444438336222899526023763743834415207190662514180019708518 +9860319477002084962218177584666884101089885263708103715532250072465740590415811526551340778700948662719303811079801949032990730 +9442895797928578191430950680716135775881506952099465712614129706777971670822951940768332168877767587109595551931372470789023062 +9676406861977772053008009279919036888396717196766804819265649832410502497794545528956267262992497398344695416980354321065043902 +3430450368036376703613028439245508507778168871582511449294099014869582746432398446542812126358198966694091267937409209466063739 +3928978146389085853115437005111689057451518521401216175319263947818787743509560003599577334399518674836615112723186476720359894 +4447676769726440097553180938842032872197723469668279942479093598413513198335476789357764400869873866217837103053672831807359964 +9438439139166494727711797686854076602382501450775000543946895474100528401256910882752544466058353451178988269998012104071279388 +6796506900519353467866660536525382267232333410870940350598284296015115345501330091034294987205096937204489666809908368374224287 +2156069024262468587697645236488193119228612123037868983120993219205723939445417381491095019098927648926035392978843470867760614 +7821524721406495642677263766762922840188657758664683038649201210659149304382137313140079079190066786903508941027377579459577045 +2846438208088991099137843881611431007996513901288343001595074297103385844197112994321057890476648269334831505632974358825733750 +1231729513446989718094794150861322831069880594276720936717092445853573977523050237490193119260788535329095088882720971495287598 +6552792304258966749461747651069123750999083956204720301600561922121624443601785058830034234673249878353748610370038504030736047 +4350618220429461920496227812205841572163867546971596032105207072905191109123570652010474485250661135080242831754570708916683989 +3648963905128198988374985403745574043149005801382728738825016196793544318399117519577715497903566248991289200713376389062963733 +9079224255192675318799042558252967439629275533715026478752109034505102371733784051667760885774671501306079109448761994271014212 +1512159183483513720286072997122368280567403105173759926916954869853277451339001028186617037332009159449259382498277868408842981 +5560323178755830694146107056035341050323807669577457330458808700666012312546070466530820114714620579338594279601664840585122075 +1503852393417110615057020185323349829356846940778480270527833679702648854699330937650632301666517518142397830288536621388668145 +3710500442232649775196536006291799614616427004022039024161898799546077671371127704078583325133930080437162856065176543455363091 +9667028302235222877392627005222715604718844825560371250888081705125248380997161235203510947690674677658525524540413177350345055 +1651124581699981562124791312861972388295643691956825406823017359302629847041679857625842731954319702741028762561310737659882575 +7234973146912955319568057648697281752581304980681832088301794847222733894976160526360976917273669474157112988773823160903919773 +7236452528456462658486826400719408745377253190375111579049732814880300278553197889206203166333133501206064778109147665779882391 +7682375338032730875672864027350071637472437979382544031201751667224513438710165103834022186623616196654886225240056588996350179 +0853897451837131243025169955161135908558413022976148659863288496875873894764829106676107357256982339087954980489000376844916919 +8360603574420141848805577433559168689130304284043047944444385048148193572183817853604188280975779635124804366554607129505815592 +5620546941760373757081442569363535705009166704002199788324439031196454334794954999272772623824166956059123197810751221134756232 +8439866566091270892187388961159863817415914847036125600694319372852752155914048990490541073567566380258061226260248539927574844 +3029318850407273236073024405659942071591211229297986931569472429177060698779317028894343293006454758184946754805713329630249820 +2687678405796666588388227397973494711846620655765600330508932225839545844068520955016865194693435829894249625192750034081363961 +0826642719445444861688478619229100303043863369218122546764063288271554588608667295145429271395132923307142550915748046259216532 +0468573883399305338589054269280928735368128284996899601120318736730996079626128417354949909634397399033464297120851931573621836 +3410567460401768661525344506338831729399382001213762232285178082217011433200810491330007874531405259479829710109345021089321397 +4652171379208756546308171186440327278368902304830062446989533198226580273246735128676055302820200408293662986737676930578153746 +2150739333645551554065559172680067798805779396460259578671413082802669062176930977818789038399646027209326563780688854879437364 +5947149323195596075203471654735880096964253215673415065855952449622950653788545812330777657299303345667765138612882163698466977 +2308935140239438613941108953213493640813697893782475635075053824082277617694735156671731366529539638010664196515846143425205110 +8579861845970706058547725114635120976593327222009873742050798971923964666256857891243866172081317410728590091574945331275284201 +6824605749924142731964725200957618055202505975317938345133949930752079669713432776367891693447902213677009365812998801001720273 +9233059182009261025428591058709759978362385968915457411652886788541048883340218037203772730667179361924214082717915090369829831 +2313518106392278036401430877932921941234750269539064717318812526416633477902124303231275087598527988161235948020286385526463743 +3838476810771838510370563675061364260967203242139687527568417240328961372800287193798489828795160993501686735937478711295496291 +1519858055532723186083206022574209495517859134830555261865370844903580200999194798031588523243896559267197627522165919682799739 +9373974604821907639636032716761671130085088834596447375314712571767917652624045445822422980937123575808022402072804691330745798 +1179538903767384713618264919378659303577920743409425206952169277638505227558099310711226481165660388253070855325241107221381882 +1789290788643749161623308260296145948321831584631161915478987224126348775962249568727630631570400533850489485693171250304745466 +6433974998050768158826396562741859990471614366251286876757878787725794466582791436968613297526831824776182286993388108679611273 +6110623308937612049537824555231489221014173246666865428966654501963681680683054782880195354576356714786682961340119163196471017 +7065396261977852039518801715732959486507529242741135438482938793950709913003256496248997903954928882444470989824659787821204683 +9252764834270969572461432198412629509667322770306467304657216423067070489844755342237360066917220335677138645076622463524222641 +2863822477918798068216749348395676325852006154849615936810666235080045937062299799315963185719888255745874252084645488043646414 +7004033498629565919058730350299817723186869899372617107591275993421694639356397475575742082851089041949288607800654160534708316 +4190299271083535674187523239133194914262331993315335908926599556351517813335151871562105201268635100775530492262605262924734429 +9412474523205479108160235037378746252342301320435142535429384588026050874475919119800213524490595720796240406073642925771858154 +2913084106745765525951041794109785827673640207212541754087108553407118981892598453634040675583256537303820724542193091642590800 +6534476025728340988278166932729090239881218889572681282009000341783806869952125287383734393447529192404697677486779298438469322 +2829214412991708861478313576349293840205174852775802339069812401323737815710652651384534932732051408295532745074816386764349267 +7099631393624915897100017625766879968866232677413779562258136847735611193368355358377164844416154018729080207420593111993431521 +9679117084184583456113996444214687961259734088670459741393316532022602071869803421115792325270648228861169427886111180946003583 +9793622141632553469977231970499144123659775068372909299552862631976151882730222347477076919808461706462627308819362671717147651 +8286017842303147776294340423265613507963077365773181170674484922475010476392279295555883161702991597407378465228254838715678283 +7139981166981912432328248939664037584747665029041762831645786266577343138070260044935844528507385472563673503510303215480074901 +6952789894317576895242795097137355300336216988450939924501915977563360873810170551299455217264567889345472118057178061331257728 +1694193485067381935776175208128377412895577843695434073687471815717642677045054952476930977756497488385115713913555764776948263 +2231234433522295956531798985522781927112508008839457523109337600193193052076325806292222155384767900607744680674492935529295805 +7875818939048046279625548208487559794494231792862975497567734812838016356460114239167085114060525364762528245992233228644094327 +5021327908105698731349894818836329218148701613108177279681910593746940136925470307141167410364253643713010143547929247739518061 +9070977175221547245640857484222877237472955055880739878012461158071643167060432840330043982130511367475881160956788509827952621 +3413847694252380420294340031416724229995257917239755969134460592475590887547733435481300484633477930736275363875263891668995643 +6581430213176248276716948697514387283511867935196454364984608140452221982505162813367283133818168866214263555481101556955727420 +0078360325719735185976311476426092189177189796469613813653608015870137963763116020948507804890688620493269983909621885486957143 +5618073667256477516793467657312199638612399475873899685969826531992817695372605952175192740133201709617218497592077143464993595 +9686528263740598514413702973825670801929614448327903726180967797157579946183718568106283503561970266617924646393019216381949796 +7239480687825534075396618625255444191589146664116441837547061247242846107878949219756732241373337771333630723471570758001480280 +2126415066562760335451168640052256439621013367706107505889219639609857343988288214828754545448713238798703235273922756046005812 +0281498418252644767093639973704165385252675952065796869316299823450944375075913221341612192647317518382010547568017829174497579 +1057968057525848488438423398887379257534282986505898261044295544509349907137424691109525427205047296410961152317113953498675027 +8037990675600906293845109738467043233236225877257801109348395466430705164075214123065322814006154190811607337254708305938085250 +7210750787555252394437778367913891376849871377061775971887348544361550409572803722815881181222850658661431069707417398802536368 +3948215469610248733031315237243553696794016591253408216711885250561262516305255457076505242344244633141474145222490778655694723 +6617637025342831507631846476937783337058482960397621355078541340935495458638237325584632545957068938913331687735591931648307374 +8523372206558862816505119548118888594916440410353297374883874035661877591624592793870237386066600870369847319996357899821848884 +2706665246531670922861049993051412169291896643639078882262700663889473964251112689202969447822598990637782956377227324903867608 +4848299459820567898610921948177867341511272056149848749209193175952272681698383112955078617288403697439760095671946801857673079 +1356542472568516852079785586485413072353853487441642185000260635680413178133480855130718255056119475830508178140915142512386038 +6695877448601376560251771173382757011161242171039619918232438391997384640483554361477105518555170292200647684746099630559477876 +3728050473071484393708670159181207753885192509059591605951929761162380613874243004953769191772521075466929499282741107419333117 +7606734018487546777450475520310398974869459655169606480072758933265326533542081878074199816338268783853568241202511239916973258 +1633835831994903654810299535109070687398984404650301845605445387393310102422388713024846802629758902868815270339924811537344859 +8928571046801342106880818013492293682607259787490363620233716285093685649713330924728743681650501355104561967670012038426873625 +9125730098479474089046067867529096250921613432172766348896492595710398358975815806788579245423344223729093128465048555061302778 +3282236766431701005576011421891586267683372954054171332350103840612998244535378427127331925639537865313201470923709392166490250 +0060953065190643840375181038485605085116833098863859869877736596835368776079089936760138916464834786551291455693169964704950417 +7199761056487159527818318808980388404160177710661891595418127883387542011797165555082211982772990941128288012248134317959535274 +3151702008500412396300814816932022272142827209737059555958537947102106965289850740478085447869942031637107833939492398930644696 +5466978918720768258413199259613003866496795654264247159666752558539556973496507886748391341566752558990139978324598436936793928 +7875629494705039695595741518975797423129800053333398650746169290789301147238968164288511049704954808998463415048261417058627843 +3675014761845381637345570687526385375856012330298649384510964329070850231207976330807099687112778366295062201878036276787748707 +4186642715813159153197640948334883431130155154184492235081536887094317988019087470651733545171824399201735161775652002448517265 +4661100517204949080442753518341612732523752379970048081237708561434446413436675964518402865361477156495973338379138615279891674 +3104047228288563492721376497746751756302186258285396146017997376211045579689813619459200950120798719227167890408329425880640799 +8361120737152440781721610389840480574105404073477545436637229579684528270718993412816918987324414005130598864358095704485655816 +2635022304411690520549036888248912012839104866750669849182202670549996591599148236127250813538958602763034624939004690945598644 +7161693686069093177957527866565203934639363610988615731752113714098234889877144865056494208717095238090181742851725619039913679 +7926731919437873880030756176008264549114452715998481120029937345677673326677605995613852123446385084312717926880869469763207023 +2138495852046091769881664603146562927499752742224823433380060312750362684368906102817542844818392822621059272739767561472497448 +3487585927757204836571198808541456058695288628326581580540815649602826692062251298954681717253220499784757374087955966951533070 +4467286406109149470022526422047665420981641926169730080064026643952810388751979407214952628713107539097869076094466606508205808 +0329251645328585224993793572099403208481085245076448698022757086335438075955415420248533451214205820043135499362946801092902796 +0976117055234786919615481110380099853162437989837673156044051970682580784609511428623279845514623277521398030431160849037450740 +8697358783561542570626868871304762629606530282055602229180460161462702998318108546783149370269213126706148977779508293460733304 +5685401232626893164468498498310349947377219975132662081047738432680920912627837266028376099158214591714411685177672935669560017 +1991014855525662209159171891334726717439346791115150429082237201563017187579488089876803702248258879215762021955798945536843228 +3358779497231258973228903279307787602968091467986183211284685640196892409994105852728310998830904989480714242762651865855421586 +5348281152141939663360571564939207399533427205490276937846050282692131755481373457167966144301656498283646918663467663563230116 +0442219576879355438661813222738756969322895770177941048443578213219939881424295242319200710244313116242014329915750902609753298 +6880215129386746871145799105266743824027120004725079746962555401718867449525806260178086747751584880447035314395435465651034570 +5590819486053989106201536213480377379276227468376756122798396628016810991489235173054754920663273388709967201645610835786499004 +9939124283008389379680409146709896607754411605235108861950292912142332714272148567105156028757875660670111321491114463297009230 +2097548826892083113543893580713808393001576983724545670281722041343097928800752617116438879952016563025042978404320211936405203 +6082731184886898638758782020161455765775544117220425974704422223842521161418889571069655363278462600259872240060382048674290190 +2889620681353481574769346958935531281292735580442822115141047535414968598234054161580569343948696497621906027825367798221210232 +0975609392326975398368477771498401808897612300017559800926232766502316691201583773217274029458304062931758320244725915302928993 +2656060793106179140097207671393073878144623719159743370358884637219392258120064605880204498430784571518779561821815030688040645 +5261532754299991261842655852754335348627643855237017342819255089240352732014055394202970458645445920979909498339363437891353307 +2437642062562451324051185886722830951326647012880268770841964036842838215823451274151497274115272960818748924818830295293023164 +6427726746699539771500247174272715628837777269348318525014112099038948379809971189523179740600410427829712055609005231760641337 +2123549160662241455227582001148252334968910254759170985955722387116765936697038502260200822286774138322324033501219326909463806 +0821193700130194457116090490985590394866182284594574006494500813138155877966705322490599457628280712279954157952750704655060385 +3280391474844631197279842080980749627555722515522776772617462578270115902754706089142103829738218939451926197117850790818482759 +1294311329582945102206118794968530880988413255047180804846373165792608079444603588122636074961065406623564621335429415769062656 +8977139076449153307860216027207885939587708357239425724499000050811065643249463642868728678159848609219920477201880783130814232 +6033248813331250043643381559597258568598550852601730339407737331740193572873459479638405202147132462334764720678461849283231967 +5244706826495860145056785602766589976181173320167438082817365766859974115630195914700440671364272919565408159761417527099133015 +5244648245024168084347025892989203801674105350823195087890804798433093162301085522657402786073312465657131861678950736152896502 +7020998265858326008838932594312667517103689364315007929663379806725932670838335332530002316590322181497959301133051038639381171 +3173858772156448895864459261457987925785493055610741437909826143835126197998316368571824712159171580331713288750535891179500215 +2461289349520625956975133669730507522155477555105599302736241191876700580974296813158063100071375109901417730679844357856593047 +7673877787687493268983420698315905381260558449663590054448103444553449366277579160597491777336624754840213430260072450113764481 +2114017564926717940676432504294232477129802350412122813350795229854770687737407776153546967564248142801354985562159014016660409 +7639567903574424627121988012936196690968952173335045547572877439937623104537739475753166073603089257419976655666993295754279243 +5512010810515840583007802011584440810442039388430026796415104195890917308156926213851922793907826473532250186114497099571038088 +1090443322487112398586196562777035714693242567109619766467418285811180453954969091778067062185341924208029182725946749209676264 +9663462321929487207790152045012463956298339314526151434668136673612071349597053270833334450801647517402244793354108502290993012 +7465047517200443480317342789716535487720038254767800382013521445375492608318405493546080410946096145404154482214436078102318897 +3241343339884810074493047134584332154632775327907375423171218727031001503885369275032870180181698599353279959939076928392091092 +3566208126745756363429701058439360596278930537654250601183842796400352091131220495755538686838579076983412474092980588063067246 +4135280622573899686743659033428766204732217042537180031329938266218979406566474637509065330050474399644477629470645922989131608 +7243489726801527985565622807882110752891684081535249425920277363697188485817835521822988850811873532635201983418016593214492469 +8143017528502801938786431108109170005870927726570176129291655180782741998755590156671788445329312481442984040900285747284336895 +5565100360014106030272353666080827357535103165363399802552515662750922553611500390854921288593734087100792149327574407938377815 +4240318921296779247997230590857031671134516419731819431863267883435154179769593315054293181367248263035923946261410558920492931 +3065344926131172598974832603770803242453631170305261893085619634353617662701712503317939637731383890741691116709803904180847998 +9640635307154881695674450558215122808610162087865073261126743708282066313632270460083280371892143332906784831299555537946109939 +0645039315913940793949968013785570770789031780794104310782057130859521600859343415595288573910289771410236190253921849657190998 +2817116940059523621717397073394274259776453515913607417038665482821641102656240840561075406926383495205691566077252073640483438 +6876828215210014803339974347992473188369840859181281062746399956666218336920552262054357048613213862945863515439475503711072752 +1206178405392180823761458212656703549891855527651389518191357531805984535259972031066177734136716154598488260571189715523538978 +4990265766954047391630449528696946350646833442858182431131915610086870866589089866607870181211091256487223226710174280311854030 +6161821498017905298626518871081902325899313379230902725132275803649959728622670760552023317532682229232464170889758455581169141 +2416184842590677978922625659052895162548385411298782741233394356631320019382233371212037232115448059660819419247622831630454503 +8975030577721689746153882576268854827896373568358085985637110827886139292888578025729952622899410488518168089967179203288999038 +0077221628423351964468120440528719933928163566191359047160562478933063353123985611799118577768774930246047770557959247998776831 +1721135782157358288734205102478591394569267133297228431858377971925993589071779657700502192679741782178657102555324869244446359 +7869917278483084269980405860197759252589350638010408763257025389708116328761797581159680452123249311643134587279719495772936617 +8130672462738712860454774853313086658312258840823449860002088944658955778609509469061098214154416089413962879973327200377762688 +8704019840879290058261002736123709853095591291334597972059260229183193853550672628813904508834589255323455601476677738648315934 +3234707347670711573887321944356094703845291499731243740116195333742959154260950396149564697796845304852992191344817095433298019 +4556001053283765367945967785584518418747873282740299536406596053579536242035383267115157980628785476579854884310834210027387315 +3952399660098012461635956108592408478331533765191761940733106633178973207757390760027945616808844645166556348572120907508405281 +9620717181599113391181876705169833156639227504083941308593882555592005786644351550764764616222603704206370090556397818828816448 +1082150428965180471148236660642979232846204673076692680655912004876027643170213363695294926023526278443457817518033465638673749 +5350172689969152071949149368789531895369178652578561324968877842847433493285130074155790255799028895045897756190837543509860775 +8482306198938020753154634340975595193686061652677880586337892305604321362120530299145893988789835870281018804163394448979511499 +7488870885383052769468046156937025798795341295237597526911841052145121608390932943180983640617731865473642038426653712735002335 +9011612227214170561522052833657180847394409367216727282193072678838450183604374539578667642858779645471672950276478435834423943 +0042627888400396166577808028511674909710254329416152368725303977021338640330576476481308582201268866056584880471857573435293178 +9268879578435020158729300163317051184609265956929202720052876516659404370185319984314639348524347471621091689443144604058655054 +5852357719309697687694316961668030847489545691322959818739069805501234210837740692288860145679956618287254425865421435945272722 +7947183003454715593442541393266684657719476151125892217474788448864376694397034056195614730803146207684810002655419508268188406 +0212392708824827732017576889397138488570370042723523923833128481673477500325568912519958461261978143669880070457987816169424188 +1965090700335288348865794109082688653306331467742286624350702924857750689705564771477934006408695884251431946386364556639034176 +6475202723487206164676214732339055881658522754087991356304799053722702797567004357975452415295767243300651018833595864642447417 +4933751846153559955643592816488612142322342560545913045826692544542626150928640726729092043333739064265750508139974673275834713 +2427576527067163076789990338481516933446902541780767233056948866977560689363984682741766823172377515643032993191346658956321461 +7240324801931427740710853207105854320415521035336023346171992089619399728628105888007254180361185035803733126321368733856723085 +1503702321045090573654976317248889826950138261936742489712456263150062608622387827772363799121813242734818379753299053724286228 +5076095412377646585652542244868299631686826272014334350505998081702990345584728215380800687580035780236658004071510394469994071 +9933250676927268802066614172669848689165401909644815070604643321993975291059605403370117318954869373816750869659405704139511279 +1490487519262534395623800813247168885091994099496258142463552140723866858712657283749576144012562970919330051773400199590563572 +8265606609996094143464919138293434901647769730550988047626306005465014764814728223656959001378552960458572921902571777313119856 +0301901698751025073371353819870024706444286250847057963018585025053110220589734085318599463450687736352183212884401020231957087 +4456959908526601850236631528599915374848039675582388183944917813443948321814362879113998039695208714072018715891598727749400184 +7983572039054110543265312946188122716587941443406593149730482849110607363599698686119685785597056173835119438876589991354302327 +0157777305454082280716591110467065838967277498987788761274920512893356487762316080383017175970582934458813655903151031833258038 +6820568297362586686149767416323236376371752928724979488399337093603770184434877480032885947231535650930077949030676059598788698 +8108972963805156390445400882624903314064494634772981427763122632358977184343156109534055951999238879890515883719506599847044143 +5178486334616508289086769240536520705517396788807705586482419714104331671474772634945045486037353039747544411584344166691513962 +8343543694892040940224135766807400138072678909751563547074645700842179499138145819361180694589974178543789544801347334713872045 +9708545131525935820756667264787954116125739691093797584888706242100925330533736446977764101332858478855148268301440155677807429 +8363906279880507853775707450188819239306527160717217937065519073363784713378967477694756526571005707114651691767780955073859818 +5651083308096512159550753176200623584419958971162109603579598590262428369134046173758247656339396228143231420340091898293104843 +4302402395996304257050404831355580727247848379655438560342120245630874884033522016540414633929586663157231554069390318916865498 +2822530065271711681487170629161962249557718383135273443686990757846857063424817696146359531243469019949939521304535035359286967 +4074330918579124906980668220696463605660380759667033381676449310911291881680221895239419422515427861193964219305035700436309668 +3698935243696912496491762756802269973014707849066246048122302860046406677238810686973357352423777172732249145383686882436002629 +2170964033471347644711273905006241361125626168342597916481066003694402986372839549149179850327741026797871619199281396638967050 +3740578777458341247832731137954141104761544067127564384269992513879476807275980026551471675919580563967451050762083496336212971 +7234069436162575416758566662736313504628826119312240011212647893254386199394411434394192850902621006968244175625150393585420756 +0819505443586556219267270131584242241933754418772049304646166400049758424842433111724173775260453475529050201556290830854251309 +8694504279853225298817987700209881068598742151871017058490633074185456825579641586256200310473031416146096103034054724162968499 +1678422417003132262043356103744836118876129032014712571839148276942048121918851516621273265839886300177612597618998212159348375 +2526979362189085413641977979132635792430656327264789820332281372901762624494315668746921549021511066663733746233883697857770994 +0998995661761657851385372991367035240872501467476816162857262794643509591735415040438155899974797001865973911239317650890930815 +0189734426599237495702149267240934080052773268635893422129378135841786547348314154856133018855495625125449892981908291205858083 +1077322089308800545276469380562276682750368988122928137210856581036772006900205486477908581832538965073059726766472615915649559 +7595664231420716081656297104542741477179939201745233207227457362234020070465865805626851389405073254933773440235630530654929440 +4561326689796974026193323140357607026602878702428514108628434286473624453127013395116455824218643518830181373377505009554553648 +5175104916514700448781338224183451234036582197678358641053766734457042147404203339056493172875783393464733980674769043154128903 +9625053130166787806916253108452498110678784874573415439254136465274322837153616892957540974054903367967131773174510386204520192 +4708907023698793329968287054491892003035884220479471761449794976142360667172403302792534515419470234070842851218805657152102504 +5165815294615880424810698811972894519218327570376404414484865603951627216319921021460205245622341179697179353800333764741713858 +3375832459808523959735342163302977206974789134934574309888193442489681770595931407566150654834927560855722717629843449379732533 +0696873250701946777504053721122628669489758761941472800177248115996803185013999823071443413470671100299081264402635064626229095 +8025269049201568533557667082941257090329107268081485134499591411578665111542402290969079825247733361568938034890137999482482426 +8413571137578802277713985161717245739847221863546744280234586017009016274669615970285654793200770322063367655004275873714400764 +0799707402410991916198286689965775278824664119744877317308731118045964149958722032731308079677323234905411311426216785693702443 +4397846375057495792019955808977919911307473873070308655227892822037825751568866737076516050806909779275729314309462325310751161 +5878515311252357335941687228452716020488777361851148660668041257248641752627888839374533734775241618706657533250097255962909759 +7924103963089477116974416014956752830485444377913838660792557425488769129517146048639663168698011387132583363962130759916380103 +8854627858331158799334753893263279859800213644437374816465531701511843970386899153300191498875238863924442400115037208900760391 +6931077305054934679510399257137017692212293765671016977117885559987800112104522229358688690556795828786584220491722127422607686 +1161689224772384204986305992766453244735906304538266466175104130240871760142886069723737054629941552777930420547825827115890782 +1184882323411827620265622192040661772065557335415769691870166844416649885380327832148533301202237904520864333379649271453239928 +2427936123038564183450762355240415926776086706660362490151633725587501086167861442974904906015877742003232322938651897499721504 +7575735828907721001810713381085000197583517056663976862531026160720187808215284714716227545242555746984605470134836970810868686 +7690274030579086519662066594780770842382679015250578750543674207604336237491570959207826549369435808803521562398005006071237827 +1591235570232351829740515082573934645171861807176059621044839781347865804129548447089911918892970375115928184446756254541226685 +2615960213766977626603096139489045595459731295282305420627566186367919926990597793979775395219871115824222384407322704217828184 +7269890378837649922156673232477845852726935996940584696490442582150880559450498906939124023442999694271016368614887466665480687 +2492299648986452580995323839604907135499552296977074591849011346778726414701224990442027943081442825619102646860492552917507521 +1791849743502515995886083914654782544108637930226038487802556564179211481768559160141129157276978288393204371047755590042629364 +2476979194376897909780308753936290021359131405027064825271771828980164040364357335094434041723555965477129434483174161190644661 +8416023154844136801169958073248822091441560069684654659045233353502161929981208692256561137140555110119558013208216615513472071 +8088260724856516409808086622373800856820389920242427841076949364650960501816473316120878023138924750918248626057879988336142195 +8686116426096280530843620941389111299655341997827197141570086674404151699053951971920289530070752071000490045958486626799786432 +6549357623845124371617655875408646869879278421775346320373177073222854384021377119568271293703313232299103659136776571632693838 +6591886804108118051730248844194375009874244915537509136047158661056246282742880964703031154340229700891206754898355101039366979 +7268446901106376070498745112524561027602391892725887518917435705098580461006376837385629028194839014124169131891549609455012171 +2285131899533028794555938374512548939747904214953588004250694538667660139983891321230034509152717542342156366790660293952707870 +7433656872059365457021513060668191880971221287545847083471271690608548455879533083319220177538857519457319889888751900959653068 +9242848205306926609548172769597825608518368367889673409305233019839916718283755877093610440755381268416027885226989117854626319 +8543014996495123544046729990961256012932771211075541381188839964005105738775284593953696325073738652709607173130510895811417458 +1100281056074250163495442070917233405800039282178047058927490789657980634481405379098411400723381012395226453993698124096414583 +1497707784106353163476884094382224594079935684207379351580860836917141337699813295106036949622008098195962892923132082743352822 +0227639803444432864090266134712772762851750164363159986164200332551145986599690734005702172852457747000704439537884708919681464 +3297805927942628853699179019515733067501114704121488080361171310117107236329567894778609250864163044875868270159183178936230020 +5632160100910553597455671868785895773407892040726468337688933116567628214043646404787040937392191211462332257825317687874338725 +2296104873433207092085115555734422123208784998029195153841807872132818719198275132977492788603520666575735728468601968901331765 +8507664661851267844375807200747151263758160914796423476220162799279781135476440706809418288627923653896822426541736985486808537 +8804577755163112469882477676592666556395113447285604375941404675578061757730014398552781158003833295830499727417346600077947853 +5831914326747916706642066586312780276779650520761789440673709492335505292573240738337240722730985752273930258542997960743098240 +0068020053146252182779250786628431011150801984991349353593228652822215156799328870412166924618702218796377703357100331644399678 +8109188902158713215146842638263856459757440491023323555585167449490157093903282451159411558188534514053282926080246371635200972 +1083689238510925211444565358062724034393858074119865447872227462830480815166007221130324188970568012551002420122076677588668537 +8348566408824119448641450955381151117598257925347235252204110951120532544649854733446808547885490496232547849386772180981787081 +4560676276145414580213909440734926632475864478909927595616312861612204269485671554044668485757818928586611911258131650112268582 +6442754729881254198230970045300311192828701003356370115227215491493010569367089158620723231028014912810350816540331381099940995 +2865041663295338237509904105995789610569727200605222728767332785967873952215814407401152907766797309671425581760149058652925760 +4017820912206849712695013871311183547029336150750415340954915312119753021914893470116876327839429907255892388374033828544982922 +6915440546354439943941953788638484102813214756761743923515532313884486004173505190595536349294404018810183152351908266279594331 +2928244470990795144278024903861617956853603900951440210459677120550845092043556188794480813102688176816346489991301120405759270 +1186435866287315352826686449165308561598819563912734578580650689933009781657638259105653726859937343022833693021987646952723005 +8712542566252718904406054272317438859093977264257379132707981296910101101713844533871886378161442772031073848272459192591830784 +1058110999534770164492882010801967465978671453250245066874219221865094533085057504007502380425647847612715483754244102708112443 +1742666667868535721098914454487184166505673192687612783065836658057498575235907333228055857705494275550823062941047285844371213 +4854496575428807093051119901176270909165842784911651361479733219811770760153017103816762799215219103460967340988812865617828044 +1917561105792307812885764646858740340976693913696981626941718101122829669609931847477816007131803415395929587982744622533841587 +9667546614449025826471505952967761841638682554759010436234535597997076544716136876213634826200883843816631177929989595634824997 +9075236896089146108414434690485647098336762127620316996183949539422311636347004018452729032082455756224021955521110115243171929 +7022352504520236787549133408418756692328586206498166126138700604093881377702931259764322041155662852059678496015745189492835117 +7091800587591451755394603236816915477651467511713772374848670866752841854518597403280700140135675063538245814642015579501650431 +3475133641567131461159293976535068676192194831897897611380261311134218099223461446586431446131415308645022873899564559364425282 +3789965382702942084656483383865958903699057640653956948168423022124988840895696969117651267238844593486453639829783832289036588 +1651772364049655351227109534629863148340655489696054950189023140607819232747963074764923819690470465024081626771998338862194589 +8535421442026745607190566634422441543153174467912420591988970152959270609189415618141521453452531554132027545470737271909694248 +4266576066028018898848004953928316732798903447774832407967130237423961865629656870579836679541833228863492849112635373253768730 +9318620813778397458907750295204099889575365028367626038475757355404525059311775107234951920852829456542067713455662881377001712 +9660563316063581065664074432670545816492979485719464785114250983280531864483884025594604925138921571079067953012249664289286107 +4733006686229685087965270923863015490601420365854224908680910247314401508516820945614321593880289181386292705522860502467700146 +9595467875394655716250868523623848001720836247002935008259127820208612390799061964695134992188932480652490964335096936081240148 +1812489955447149372964288932685703059456499125726325849419752698359991222537673326627718506868098338620828381688669635638529284 +2277365318750471576552640984213353147488350298228497735938404810800516562727031985237579073707417430708173600319624539956749453 +8558865515990107426689500711926656694092773040192992037548490908808858324669008603563963750417300374317505710614289622964156217 +2457409596680804630516887336784028949728779145781319560216200606134423822190318186800036885710141555957030479473061434184735920 +9100569805069607595713477651532945125488629510591455102444809609662844669593112401315706794492448332303051585825039449951564890 +2934068975480357331702373570405176114572488785747724819226534222897314789801301206100799457068445592290174980265109976510186964 +6974436264361115896790352170073260547419486352304459996628335808264941225228110927615548124284539746249063361590443246656233810 +2022987197590236256417410387584617910709314684989417286318087666015073752226802244527631727840148104686143554769788772100971964 +3931314203567669848668014170818609543271827823841957939988437792149776879965413983671414699512124596470803895106449229017252962 +1646897058572253319802460817369397398067528887800488520057974357980724071070615260303277654527720835652573050782049698374251316 +0612949136289175984348022991134845911118529208723293255584972808307452158666230105816460375784187988904522880654792018341929152 +6230320383294577758148209875436786980246088588555018398742402423700033755813061790265512976391765915810238317924960933176044915 +6724326703636717615730930803229989144709304487765743249773045992631205387565818549594275877262188712201760168274734486308418567 +9793449160017973876181891146468573134118620184336387033315592292444997748610681437980592831049932965491128002640896459624743912 +4292773888056347657265747923515456543197563688072118373801968694939175467616839381912904118459023550508906592936405878500629369 +0726575398511990054804247570050536297610421442312874646372000460857738421341575986454091454129764640046210072544927787587872609 +6223497359073574286258812426783136238045789247117981734256875260415121593723556039489844073919163685845325053278772517808357882 +4805353931456319684552807410713836603840908704132137938671395582768618313934438378980539779778185823334069713366782891327854771 +6127074671224214243978383308821083055917752075116334068237357481944923919216084154202678626979129283869929664457025576182992309 +9095642603811777857467738034071749226163187428061948529437320054852470403172207781014210493833697169288095448800181203251014035 +4736481344099852996408105951657679269262847981157582390462101509329835494113215114942443801348682136828613264161536188227115466 +4003055863718708771225642500990071025833297545571403747979992978828908605722348616364122656556369792934466258936918430294297508 +0884716108700884651920742851823456839844922966806960079344733214332219603718719129110850475334654765668025057309956984350345154 +4885236271502923816983822161323233481126598003985902081480145736364185121032980985643061033399360057443356862260970037248156868 +9095109506937315402103438551736664529148909771957666146775454439817301965157339466426336556934667643823128218330998524081566484 +9554172468178139926024925040064147501158211448170728837042601105476806693933691272219558658718966118313273471191896511638062325 +9954369358233682690458005171274421730241048095790588467590844502525588085948164312745806267036443692262270244497152879755376313 +8540761990884030057918282213961331175575293790886064372121411245265558611444044299259702033859938015653338871057063134186390567 +2937295853774855369815055903708691070046531641376115545763368983982874877046797588826386810643172084809422213611867667864377575 +7456129086242673800167308357557286842740029636829456648911252509981444991813390655015368895052836323823418477931875989888413494 +7619903401290202996353920197163165845104216512134977822317453208086977782938058360962563863616931222677006031494617743246676098 +8619837607929254793532399964916863199570819754247459608169557658376643077622610923724449371091028101105519894212018425361113161 +0198232737234871539712619394554173033293046636313472444724663228704935678432306609229598127499957898688691002287353410535746129 +9160344825935915883479808009630827527538308033068458669327511758149831271072074138691690968888119139014429832927001663077896093 +6075520157463461341098226057137016444093714810741234441217965271334334440753561468859945005901907007749818800609799068435785758 +0546092246435123497159753904346692263278071121042508101131684892541656872975812647089730436015791483195690674123772749878087282 +6758449872382291050831212201949792798424861556134983601310627614120558124555112900385099751123167702480059628861813816928572678 +2294380967592455168636104873737766456888729486890507823276579714836828251933669783531511408873018452811541618386657467835047578 +9771964953738092240932562360117185595020635073758269035209960005817272212266420970453644822258523689081214708732161974226486482 +2078896042427351080221479050978223463317038184395717248909464597626365437809427793625231151897894098417132582150723722131356816 +2733651127665461066112679225653017158187022844835577848138599970119201959393371465576981148627618500453766449113619920556794832 +3098796406035170187586479893078215549068777188615428168013041309627717475700524281634638706348659216956344427373631234955931788 +8266607652077744997152631910857696622624195835857414262086680438204284388121731692389911117940177851680318293050972437430080491 +5263662912508589659968304863826115756592152285582835871560573052117732379831943224939230850768149596127803763098910581308889239 +1399259544384714811458348148272122374447108528351486026748375991519038080289258112233626298579932128994892803010854830711867003 +0120562601270662181088525907457246972630366755109340652778459673777734310903812432648671068725042051752322546900077059823883777 +1071975377081858251465844724729282629412278512504579575631776161691808267150394386844729953211628319505799528227342615586982667 +5142190253907427483211782117002935160362621628506088192922092212154140185826659904555543974037176549370222198088358512119912742 +7709122677369521964052899819214134971313049580616225982506654419124826044743127724976092244678406630989264402052401911579629855 +5241083294500875288635504487164898666527862404520007162909767182104068330196614730977680503144106835763690135404371824827295654 +6210246422372066059297630639990103921525353988425004032130811899385175353438423745075299078297982811812894692845392714907314914 +6639687154171290857100923298064297354897427312066201467165805288117452112109293854180789331988821851079333157010919824197308857 +2725453575267288288994357675260613771455378866560128031996095809604727908410382843600101001039972876558637948184365207490031492 +3022829447684501701771651529957077694566855215179433255048106574130176149607854191690519660804326926806696105896923466573489014 +2276500150756721393101309310240889850130747890241895231090329328679796493201088708899083812272168509412763541079565359287160502 +0441480433094880222135017379174984153891031352882663477623197838966247123264644246447696041370087767283894667315757217159981846 +6600209627389381111032923919248323966625789375205223637463959729585017236548352912218466643214096190786355523902607233701011308 +0879209770503108176937425508560832114089876275039410069927508813642206128497418765988069857902212785710790868994707145334746457 +1573213691519886100372421851125709104684878670999887084420803400476797705625591520633797149282934213606570900108998582064311163 +8676807063736715112005822408555267407647050728641117839753641320386536310057649950460984214505375532528456592558681408856748600 +0458633214841398749014431880145127111888700604235741500367092065118505389545560189317863466505700948103428117858933891140917600 +5132202176401630576193346962510769965520088034746560321030328807272795203005246723655044384670879356197070618995577734499503916 +6506939563329550580672846170456665825955418030610189100857896112174782009029170826779986506077333681379230762539236049071683000 +6370393487113254502942284063528147627172601717639450424195980075047464787096908959060881173708540962987133431304917906118868338 +9267746888420071021500735075663336736361717345091433424161991124952838871022030399503645263408791327784786086730026429717318657 +1672147391408891002327479016767689503494648768717682253138108661180075426978036916352251329255918145397491166698770599594009604 +1973656522713866421729264353952482643307862403936073686775977710379738331755646451684387173747099995521827339722863619385237499 +5408235756969453434913224193378329404979503566466468217232986076880098924660185616188785089771941834979789440134998311199906069 +2232778818840350786605080110308895419763433233707234872571290509162878857914634432096414764357358087088039057924071148091731175 +6015400792410124384152213047000568844879712268022351230585299428537516602169557527154990684330404034064121063393300034030430905 +2306055374539312465651241264233074065955242079361211680462145344554747450796349210759636113482252702608031612245403353997125476 +3803253691034432130798363413192595243576410393487613462409388440991106589264853952016023685661448176427389232941666704057678369 +8628747721532797463638681268551173019525973277356279608142072485533442125834239673920151827309537064739870526314899247429664781 +9025827660968538606553886333865132013576294269585884802265251611104643759277860774328243246331432389944537967831541391858902940 +1930816587488360252688732499160213070813273038333861895643529140656993608246150830322861238139209540926180077253222099793719072 +8959519042048720068153629627140256241685908963439182993973204720950662335620899080859042503691987552467007737450276337233761755 +6288965328635383011799009079228464765004787324704548708119617161002013632986577780042057914052383556753286042031709306907755664 +9921350932285357024268612614215975988614776231803905241441720250462994526451363159473639866331342141233807338499617054943944238 +0578049360816576019800945237402222592171684814088734743903979539564400529178521960883785173251341874550331866956766247772470369 +6083904254700701911630159329372053705191987684688500636724033475465777760911461588784110437867357713431148790643538763108521995 +0296263770538400456769346062348737783795801732870097577376952964730500440617573963135158364912865598645263299913463769215039071 +9483488255991073328145731534454837990839423394416473517085030613349147333787972449423197209621559003595529788900553100012406747 +6566884594766054381929553636693829055015384572992260492476307544253448277903698366356427152189807274375674035466963843196695551 +6323074679661834194813723642658721376145252523971723277400835325217197190050897668239821693181091121061162219401288326146881306 +1808318984353930429816050257400279035517547964016766257303295002919848213345027516727642354899576468006486504355233740324100211 +0373421583654094261005790222006220690243740321608605292144463259644370384862300052723119124037021639917903672109294422220446746 +0968367458627898019517765631253210766036998155063276402536135475040822567588712196840514962484791203840216962030611173316284918 +6730753402413401318179055308056338575836289967262947906669540139949663260817034924092055901733213326920487904492973772740252118 +6813399504142772729322264698007839309738863435169195740088479397131114450638902610593694986422730992468798340242051077542153965 +9094257292692469011371694239749935666279240094911204646416349941755315640280190692332906803301138668428433338486284139761451437 +4927696735702519573953727266824755919361553550569177388087634691983381789617481807188380885676894329271297374663349552211867549 +6214976348118306656535007501010457882239285954427884872811741042673638154912694956412208130004776408704809831151781445974935728 +0120629582751291565009219505554682954747176012178127728953108970378564330824283571745236870527703343674549433801104649142185888 +3235814158776446629647585300745371840827518705724793256416163401064594247074494980560274282618040988687418212805930332830675213 +9481691238337366800610093527436128723725108303743340453678113695806808341554588638276662497395500403037304969340369996972707032 +4082676737625354210464419813293767706297361241754982481007296169725698649583516782177058747437891980516222464440989464874961596 +6461961406394849542189779035893533584829634128015156718397540384299215958366542622049511074988966488475073248255117221338638960 +0142933164629079463406544918744481398396204306933357431841214352733485790844296470933212820449310522157583393441447538375216436 +3436698506912676403242337919492000906428711269510381173399936468041047249569064777837314598565623718618433285256806067639636763 +8460415932660725184337524946121929924205499042071794021186039050613143778222559008526610557979170270365704047948250328090311884 +8815703597819948360630939744751577068004694199231149936335126538352407733636909351409964233765563167198074923729461436493523959 +0713926842635856417640343723268323797389202591360887205782568996458244122502082280450763638212538223700620622044815212320168684 +9492871418408066338755969200124883796242190267635257673723366249954282292979482645457308347074754036142876356477739278675875440 +2183538168589813954235091042917215997797441009968993171615399263341109892414323128649997905799043937988646881424204624530006049 +7733957096488341212048475007904035717627062565794383079540365496869348281433928986222996400495215587152772750589584231009322321 +6967167721546910451512704467642578730007590171739084668065616508498732163764275594573615283985791091469103384452900988352107000 +6807413612465354023458660940481659053886101973060578770973513038430471528121342377174140575232152423951725002933862875035962993 +5491625968951170241792359320698419197951622254963187269169519754789130928703155856710378168285319433421425146672702276206900357 +6582314544521282948303668171056488977803778991914594073284490075274341682398796202525053587573368003492350674264590216915743030 +0229180917308822559423399370500197589688253700875858499871606888817412759424516191276640847554118760685723541416666202243509736 +9274208984682823732942979222927492539292245964003720945726994325795924579380001664666341956208236538074900737136200088724900215 +5814292572624892701632682972030886871485930705868231267185916589385811869703139406294452824977282139608709779376891002163708313 +4033763100173660376690182575379995726083861081926510802579785312647154456013703372795547129639766049095465742696104516613357933 +2568315805640894925038031436747677782703800879694535494046794349191040639680313707242709239136167699797774023104845097743799234 +6763080953224487156488442945555613963349180931811315907436001825754710792409694328952798340996992622252003347436512459358128857 +3954533989335010638156841066648473526011867397359480295694595402042037502438174479721797977373598413288286342098814213128179837 +2396433612158432510452891115965816912823381888049756989510546093247222344511226918341455435352327199403241839439420506108676964 +0795850610161143815844607948060775881656537958435083754755508315612517655376939244238451149176008256028242905067513932423707050 +3517402986146524008574508829340726577308717530546869694363472906429850377209335971237767032862995433224020904386089476753728648 +7922151222682324169544546785791039508552110735905147311483313421452486749631056709157326288104257728264776617306059521303067262 +9433265821676424821718658733270406951796862690935463256100330389699577509374904038151899940074906081844482171701461900939078601 +6186077218855804555456232441080881439587655776678203701080218698681846807414980313213113823373021577317623820418853903404980853 +2057729858963790488672801591735476430626710017313638370400064539747700089967564851982820372204387613607305955240060602392589527 +0383881550897793438940267029950019121382581949345028853214775595378737481122241954444306274659828618843592228475107234699152687 +5284798962706326315885441962112759776606317588723708150855167126722288961236786090248841992912344949773249220873179712041971922 +9678187960541674562218790198419395947752158216709587927827286969325482868753908887434457039802709422399284864987575179916747347 +5327838914777072714692085263368643404584146218804037383216974773265161119155906542869845216416813947477024304569196702936579095 +6268003933265575972147374394617649572960257762413423428779104569461013465964897200866208582709538990234194911357542427621601938 +8074876525846270711334089333528645517621667077382560706349198812216415510664481057225472908752257947691952856523071940619013694 +8017368030459649790293701927587307177007662644838805113977690349437913275410750363651007980050566471766249753077608880102272532 +1982797159364965178888531580373430260986522031868350608333752657707407931000254944024943348168140524874484514493735113742249981 +6288538584949755098193652659249807271937205648283720864311553381239244146100739200355642491916783137202534161705603274089556521 +9354739749045138253043275273268328367815186805183694069007675806235940663397422177024952828447038786641822001541524569479714913 +7698528117160783953905420058390896481011235446749390616223922774275291430828661119528466868454607218473820114505896776059375354 +1619019266174818706077047840193129831420498197596777970098691437675408999864116292704117680089265101055969410774137278714249866 +3704870678985840970113166302795462489950063082099943547515245655672648446079824717316402600594084559028770660910436751665528235 +4429408197253611572221699160867353954702111945889329273311821911416970564822252007768702453855504730931208682912670990901446785 +5056501125351085290367349611566587022633517744283896529455313187439470673480194626772000636195743336806541403680260332379718964 +4062991551971333412504544251310884322830607074386974123640580284992839293238984564989258127209764459890435786526862148574118080 +2625522665812777011614768650094518338934467618573508691312860257788451631146950375887371627144471854275002143114396671788841878 +2226696461215179698643658067033045890658991544538880319431666117259518073995053382607108900045909290904771038098705696699068324 +6726532088338857549906950143621062126429461929145317128221035122460444306074528944822699784500814789341052158726046904507897034 +6793037164285887325166583356411787362893236497739544799385622176244058491240539097656877256235845151279052346581850041111149908 +4093573044381701004333825958081420923185855648955716595548583153752167241574306747073123301312447687906770815027018933422227596 +6707271839374572454938209957923490292030537274459728356418336773458317286912917431092307541994164910938497543069009665010432516 +7241600538166073336929364870095691302789706058064621204209320909673557421090888354529446748882788614331747011940455844576764284 +7137143302702161225320499515565654830535431434870632910006167776519433451398361978770695152191140156154509207190258738746324525 +7177837461622149136708945368215880972643302158259696018853584758017706472295497707280871705217770629770641478542481671610250773 +5554013978114456033660343221358867604345109625580200393288313536168463029481940254044728394046188030569648323861618169629477000 +8748988786120543881188413407361779739580428614335192009085939766971185616502526635854198592751325209395171686208426924544564152 +6617729010736669611153048037379873649289970538055287963581277014784710903080627770559078145423490012308690390159447385760636617 +1396112919025930755072794022535421762089970913319020087082933809741223660404657644541118861471741622430838236195432385726159361 +7249197652757615950334012535738132798576509936889286629270864681020298782574659494783085110319815705595177485744225974776618676 +4858557343521566530159783491610288461205535017137620369901510860776505101408698469561089138749268742702921025433191359814405089 +1405508073047405874902803040100792069570430571449445778558936794822787960786336616853548170322337310425822658832018643837559079 +7255075818431908394615699437073631405701119134607024097267578921840611418622682373870814723883284591259922905738590072345115516 +4240694967339735201081972989105259182809574142617805837465112261091318429255228721568810181664317538777929819157212160575829023 +2748237669200464565992816533713130419647283186413179673011963172187269272224647869974262327569585442612854368077691269344188871 +1222760335817459965853292578856201576938118519312855304508610105281112231513426234814422228921425942855517206703964421120719762 +5224765636718330925202872259803187360837016560225525039097597027027310508724003400835919326880989701929146674585353105826375662 +4608686356759945389820261327877835956104857982859971343770150256738677287345081654437248462809898624047205228119203349515135144 +6762635128848980939500701402251709503914581106728361156687648893186497005364492516596632908393739083848785619442884628144162198 +9408219837678921746467927680318487696309519621970987584546154535257273988915610716263345779849862915767049642549659017655936977 +2878461866210176159858735856155729710212544372367962529421415383498942572742627275020994117127943578105501057737118382232121164 +6202970140395425040275648643808519609153095822076099220917958127812733404125781257612941188098229102786208245263355552763155862 +3412899983063072356219645639174430993848691176774827540248823520470123385464397586328853608924297363157128484769714643392030260 +7516335187749776239975100738544129195651952155894618035351972235899711941435830137791970183095848641340212385537760526454996975 +9326117473414458915105300842976184452176417489508025383214692364884363250764059076859137855740438113389750789393201920849913143 +8213713819060950631004232834986002902474872793703875917119551478078092258435100422531909399249521481966638648761188280089875780 +4759150755939784604117474362263502288999681621525995527689733138120086197663894427404346527959682389904515329738583325486736756 +9325978425209872464237977527958499931962722888446298443732493719892549369851302316811008601401750818556435135705126450083049922 +6347121675883203194927076244218929133933870815129366063255106247006180593262999464959953693728515646161688550961478583928288648 +1770297585530292141472219789662846895032928913171250259285237148388628954759446094661658933173846573153793706711301693991850474 +0792606603197595851399011444059807607822829889531316870539398964731417122727677982590799094547174678976545309066723508642538395 +7542251187115005568283732133757451228935803355815247833157647444961116649664568241618566131440468159056245333148920222556848970 +1686590915600101341570938724862037843381611006851337958368672209742162426192678307570447318588843817286911928534762475716887889 +5985898442600265569905129276649109155018541224177922670883691334985077196371351903258301592600475229763499481236752268688794381 +9908334038229937596478744640386393429233365920050089472460888907538665294038594595567391991275514366380037563916695381509760722 +6697176988394487279550132960940875716670256744139528862226095599316038741474608986574869723524996958686407456560239747388319182 +5272345146409276991368562911742268892736319679968918454777104809692672080906197146972033144703725807051487037561295160467451538 +3361581138752178146561886363234614574825214993383047665369152075131490893168140965966229773832573946751662445845244243875117807 +8497747825333018837277204487016088859935993376161231128402571833027091271675038762646104326773996163504827534262519513030402352 +2850247104178367791237021165640248127529302018809122924281078393572312164207913339929888292707995450264033189457174311609184073 +8924909933371581642458243436549195910810805622486372258791244747556044533591204446068293714215938334798650520367000310196349472 +4508128384366452869820524778886955415968833938807781198993008689473057745756213279992800316467476453813822465284911857999907387 +2023953430847273884483235864052375615320236609720705596526699723117087569499174093607783752368506950667179330339328300764225536 +2114055767363508202672868934173997107264743852817603394956504753530153541861401139621693806395357957937922300502759889840467545 +0621018093521434841220357944235687355230152336239627344725027296978480489999738827553268628247847786834042126152054295485896741 +1728177905254640772617413047936979080771772313470023078251325162582234445493894126314939244900306139362255151328277418578396047 +1792422884816727055970087290849551106652457784152258102530082023142369893647618177451355631227247379577671860849853688299849551 +1175037347149672092098074310747361251964753674884333700769847744830036555394374700856740987145924794681060765600644641697283847 +9174589667717240975144336198616364140632445180996668524271118863864525314244946497799850056518633949293612226613509484575056441 +3024489420602308444377762407441001512681502589345978196662197798484057230208048085644756143263157845710447555275495197040972213 +5990095737215350840694710792788564049039561363466293476205766559255673678361573392948770517450420686662337759686497763541080151 +5536095650185550909008740808818202276021308469567387829730017248451381382214473271435330569988489076831395673641296020100168963 +8691489605876605176450990544960425812802030262532258796192840475047025803288315858498575542856190938582743335067317105863072855 +8766959566097551451268355313989562707945501704157509911384107284566676448181401117485715830603954518469086610851712800284373857 +2622452939630203014872224939693671859800249164641618680985148159351811785234412541320836473437263872332827932949209391274805175 +6798235720998149184680985250493187047397164050502295623889415695762080775856311836882958794525078302171514915009308220188324579 +7404140736104785086437030648484406734008243907361526117865122472043884228991384547907143497695706699559582484787176857952820522 +8804256451428134606481551907263647977538193140987930551038510575189609463453621867637464848455610801693723759700181203650436839 +2454914648531519297448584075603475998203868332048006750382533505496359960760723675619846780966293281981298326598027774066580197 +7995861529357273876737015678397822856184868318896981667361295506177063218216975663372922918478316465965515092121524544927046347 +7075516111646427841984461388320881450094372462158722036978724530408790912720869813138049436688811195541657354372228803333585071 +6429445107361977048898774220057765467210817429170612447918756002833100352538329968035564836996813252177717820091122268457886430 +7174078917945962391527335388096356842601813618444449251961003226992399982772853124625379285502923443657958530018722098893816397 +4966136548214340660067711135963960273659031432856630667889996071606345454141323196469871177597395097849304859547230071866679596 +3726141431238995322616590363658851982624885785492321657227849247981548495242247508727680411775990290110420891478204977360853532 +1644317907061571688813615690182418379232646792079005297020445585096706983621152261491067330546246215457831060764875633375743540 +0260037498619368766791619780583839981870124397870392302778118229013764892065306663721631976910781105530817819103416467777655922 +1257160865985307861172892171513923812618825522832093610372208245587343312911128848417617475098350804052464802003464019493811163 +3766028847159301257741274325560482246090419451830573366629957679849625493927009484554531296839025641820596875488169323390321224 +8232388226007759579919663974563407506786051531271638328419810743863932368666194478382016978871056258046863096095307264426924960 +4549834654287891305590680957281622094960361243150054432811463994295196664383435297694301203486327605844830810715344529812950660 +5753311902123482227347291010505867923405362170620911512612976729652957268599586765352146177919457594239032123428411979879139233 +1756522666058959488542110121660842887596621268641851969762507171503624064440144225359791257651455405971257235743672383114242117 +3669744927951794530124706132421697781115421405857509254735757547057701496218013526040546290811670945415111481766033696065077908 +3743986898663416455838782244658330408591406810811925113737418608085893951934341548820971030074613077943621026895167359064570606 +9471978412930979202834282673808363212715221979769900548835760002508328425519756934140019511728229374555172954389643589129389588 +9455543651731906114795743817911783078297338344457513508249293073101170733009370971042675747320086133963697038647918247059612888 +8987238012717473365026512535048463787033301244418231940438539856074603360903070483058902057658066015083363248256775852399567354 +3364502373064620236820638615612559600453180311037005397530277541350185917242503038852432559174885033399595723295543954546558084 +9899331087165498351833150053347482195081440704956752507876144131532942935103436324218551370249739155812929164520262875021494783 +6908904643032400627041773579937621950623563067061899745276566450404645141582156514162192031414915906603166745603027411059622640 +6495757281070400862553880509625330137059949973388167457480215447233006890221821337932185784669081820552176614865399261551583475 +5214987707999082292941559306056717820096495439266384956685554643672988468125971218644583921446293636673793845912080991022068721 +9098075542650837679681095355197850211686123726685262897042587789900344672369748441583193738857149324437982999889236683375639656 +3454737499177458611505297150687929879276146562076664745364918792408073476880967147036390541767431864129948395331642282070602239 +6878237499611110425456520778926623547629857959652095556385934261653929146175223058776396057712180642186976052559776640567287939 +9117825842497962507655739795549675536045986355442960959176637564697703951624943028930326642942379066897205661256857648338706258 +5607249824040048715831055763915681630425710558796489860046207669351392908027930067134903067626066854044057840603625024212216072 +6132093035960157469992103741008683953459144927130308314821395510817197195888871912314613073270790771658400690547770532514734857 +2289766523163076031024159344583085122839051296194651927355671209701571188590931542840886950235609126079848243347538568462648984 +5766639281060869652151900461538022230554309323803322083408453315192390160247383316259588161796028371801133887539960085255308323 +9483650441565836194775795637439945189737766569777908677905305601319160962603730861873961369931413625520373523170442339205096446 +6397277751314562067770288602692729550017088466152598972362685935373861021657565974015373030623318967239986491125515528075018887 +5402601339014610197678055292860126657475597634829577844878918241260173909075027791997352414032212153167271561905364752619385786 +8491863948995147633786778542070447298998068007279647829967199136460489001972567744945790789456373114064713748773853626265799228 +2370209092117301003097542080527068882722669167429990641325299940901538545419032212913930826834352316812337709858765794889107909 +1398631884718147949515763634548480975677014453715972288924837802795200463595978496786857308537512388719226166051585776739717368 +6941189534316796817726911487247546420252712714875639017653467949442416805399033991555627055669486555645886651826109264153186819 +4940516166674720818220542130511334825103331157208156275492549414655335399338455554953498955587141484886410449146234826660024826 +6659792948006838082450272697160209259339168888357685416533244048793279607628259013570705943914832132649302220555803191155769678 +3450607770354143996892622745433805449122734203977711922327068576407370897444833149206827486642681899060356549533970828759001934 +5727275520263633514968976110225542746359061074870472786303946464842833869445090010825025879958856002949152638295200941043841906 +1327175710840731608371208629917959139504834096615996636184118498113010168900528468440699292352911245996048410672241669829930509 +5804872329617982647538232786144744032881485213965459136524837612293635959247462464117508493059931748287964717902656930987863246 +4277723315738568098484054986328302182182023008609946701266751039890388797338292226205365731416488922689962862442504898912370823 +0169167783207015850910326779335592201489472916428336283983741331168324385837589731443211700684040249551018329769335204931232483 +5298537282274168905097783252545295159931996045324669229561719918373117774516092756810950028198012718738992851067743291059615923 +9444851345487696935698137109913963152214976022384665735269814043959675847649595558507091607437770303094115672250928216923641250 +2149472592686716079768844857346451000286201002122320769034758020998927559101579776094371380910427183625784710226757782544230415 +0647876642293735785152692483992423954470320088754545909378609961100700126777198798362991474951795873563734734785888619665299138 +5849838488444430649533263787538811331357728938722568950445006777515651519234515285808516388251516110101450462227962034131202241 +7276104739525907372419126181186384546119321638501786879645354354875497667502927550069197442007577146458961731206493061524033007 +9422217503140335187736221742892478886207544102369268483447607791356773560830807813772377547585429918129160635826252418480968692 +4951724274446263780114357613049442315030582695389596800003586264063614032462365896162651936067640252887621519671734335954108739 +7454566191860852039559843736923834077952568697550937244831873949705841445761551247253789687164216518186201016215799884228535871 +8439893371971012000885553951535059948361302183174942319645796198457357286423328894929588756597087846522596101434099524110623679 +2208231333330844864465745363677908966068876448934931718761801046807820975248234034617078513450119384012451530340905431793537946 +2305572597375584599916270867173159433891169944691281508143382202813205881528811308590035206333627177252785229699333403132814188 +6581817307307772248849032636830370028521484024024459152842727518740380359881146898101434552487760886134249651169919754088451088 +9972253499037895070298514770518285390402914258598519025675691003304065496834156654347189749182550625905099172508363395356804877 +5758008808025624632351941198979018660594929305107322792450818499652772086629264781844489198081813129733546688788720899531192457 +2745675328488913480621264293304705435951452309540398654081411328496424730516137398740590505211615977997709945823736263422098774 +4319217301030265407335508264753089155109074707762624338354639950177679811707826932551029618948153019495825747590381756914645981 +9125342547489286004121333337059776054538539726153714180036823804029952720452919240063140345998373975940156807239544138619226508 +7242675874264000704878339896544038336920890771642735504973073959953113142490324395737352393349120127057115852252907686376626747 +3473321596354209308933007945651010752129252885281924230373167976919072240297072208573755175225599946410500654362886750707794528 +5727589543974524706691693259117706864773673979587751324354353742658357813584415514973239116989629743388388478173856874552697761 +1142418605095869987288446285147568178401344806595851658792922110020211362874700057924021743582095514290749364426026381491938886 +7017216586801442390706943294273005287782115442259090512088524168970016375416554459606914186390125156135455842825317699271637907 +0211233315441242280840462166926685973850749956345775689925242698347596240183158491250150173733543845430700552741593194555804750 +1797724078344241125893042060239537330994558786922162379383591805537659608535521283089375039292199790021670953442916099772863332 +6130652679697033488878720295305832453720948952785209088953761883766005724826476556165454243473790630572440696210857988190704713 +4744821686581324503785124383460438165434233854467259677279955943920755162775872557500679297287992727542016808831027373389642811 +8818850180176632238377551894120301699425047464434209428644888921239546400810384063448594908229208386662084553428371435016786566 +2565402723149127183692936818466510584042955514361987237908845951130503356391102529543147109435545190864583265738886354448729716 +2555888389438655691918145095510314610669012618591908132566801282747594112804817710376968735883323783155393749243633468461076212 +4037970808854790499072817736929400553034907676890898900783090149602127450205466513482819048073679969106848951996174948205730701 +0816030875372524859030697931442206128673990318458281042368684773151576092499288971063578724086458233384930941299441451767263066 +1664521612180858396451529620088930659358500352782541669595817923485051339758066634536721977274523994998146473981701939017205602 +2524104027099484717563367769361246592695182470261402965977193164101572566284710942794698915634141627566609476347965721168530986 +4359025426573155220274777012266852482447049345363636421786714781355224872652596955092178684933842367455487342490049229104686636 +1405127824749409497462184480237538519293466992317964479311951604897323056429690265101169427550747026874617333157171121236924703 +6018076627013550295381656119236991061160617457180144979226462876301063552180250667955529552359828561142316939674528337208856344 +4374830018868998904944977293261195517662546966324518234611103690143089662138017933662034152841911712239931096922875112835748433 +0236447221655510600424621887641222992324877860049632049076965940501963553949996895966286320389211270787434721126820780971624253 +1124118658886218410732481795333541469161905682578320893049989427979158879370075562912903672274463984603712619157580009287344826 +1490073439715132975375184719480763004789606934374075237219980119217402660147346676094869754304392997432951402325274930204871342 +7507088153751893726167389642050254715794421419546083151335746591084804700733010001661585164038391983336305667211157845088930512 +9454605864819889656904342091511833717539963300409957365660591244004512939427858955522107791635547956216918186634105330346773979 +0658593516322846162016300446203238142166026181710436615937557978420890524597354295972144917810824199008158678995080211427722929 +8980589661160288330719076634082892363742482807739228207426629330991685667550752678607146578792931470069849286956087928508404855 +0981420843325337105199162550260043516249035154228170027681913983488364730292863723816365277728171439162179656110789246164875870 +7235989772218476249654125554327002628917857770756363184283174214585030441232386973067535578285982100385381595377232095167172355 +8531968481709055316167180990447601481938541581517577911736493317452245372585792259012856477270057051421673563226983021870172320 +3966519268023261060863087111821082801296148611252172465597370204173800451852480272015584093876590792853690848377385063421097933 +4667059194375637666825712724740535719642601441210189891690016272378550854485280624499553958549215837331920055819015130335235748 +5180652276625067336617174370015460185337302241346649852510792894228685353669611675801310899772318114006268028233834332313504023 +1941303813100529733477333374209673305915978659212933725574700988773740701325826021523545062039757705261878074707241548966802269 +2746067443336418197240323253838643666106109625323987238695096142699993722607024221029583744572542375113816178166737688431737737 +1790082381839115387609873262982695752275538347088304949304931589485737690198187441832377091589179048950816796427590616885843542 +5552689266955858826481326722121453449315123458065490624658389457610909531278210069460670951157478754958271396713130819467585412 +8161144774814776141255994752061769287038765060690866140571952344698969957192475271081186838639007943816699743960034503332953194 +8789192141356424274503562578603345033063547574284980009602843989229909463071658256259561315235167835094522110712803401150014304 +2269120125327746733199585385420581800688978135760399189023545691870920243025811096318243711254873352522759304218250492540601301 +3220660009367443347579325094663071621014417170436158246461857082697811419327408184450532404128001876815109034413458694764955526 +4236810019612678173215181621403086283786465950563987195979676317579127299901400746488219293603220797858502005948349760508971512 +0842456999754267165377711495312735495780613161991580964945616998690779002045181519007002606802172925174094502143404195978695837 +4416749583760585438101952975396064089728080431113199590836008764153005621794368480547875309689843049490843783015113705286745691 +0795274682904383996694242263473123412015847505963674606830368047427506205910859669522416907398900276339485220277009294111636658 +0726660439712285671104280059752886748116228091939311944983809111670167487220776456879193368274819545300958924106260054971842623 +8150764216086897115298219326429987785009191738597276238467913392040684511353140756968540557235239878544391001829270153861846092 +4383222134341106409110971398373626248863354871646454326385144812872101996077996725153807549596115873770116755372384144360289549 +8170673501302124114120744895395545744756695363267571023745570418014263093634951684729982428233593809293841917967048459961199699 +9763136038244743451806991825890314924556837135102471133555361059824564665398157293669951119702184740695499289874076409711970682 +1422421956968116348857989939957276677533229421063047970708284154758036180433546990214632661390471026182712000805513222171655488 +0790454789270387069048408785812014404576158995323548751958890039523932351478622587142638056133836919868045535455845844392024399 +6828229983228262659292226957969701095193296498963956591476120614340834793473477949399153199148731691820887892496627505449096093 +8624492093374718033878604413583151815223479591147786368391299257097621796393142945746725700896309907581595691253852161958930992 +7107729406703783687280485106218574382700030054257355689626925093581880354626104182184797971451190514868852721286584413091561305 +8741987234103316893377380144344514006198840745678799814654988835056150781397439910157073114301452768509065135515533734967678598 +2735043292667576626222020726140529203825880750760734206750175908534901067729659839678099054130592189468939769616706044680647240 +2763006819383647925527431911808115681683993973822624367816274873450252222984650251537326892288304742295886859351709349425297650 +7134753100395351301348083365117683812003850761712390745630056031799089854945233811860729771699517948808663082113548033370410055 +2156718353193091760663949540389192861228968135157606923907872289234299014121301709409557692508414639141148641363055713300497167 +9323046502608972228620000366263176485575290223984881067717695579312796433503231136630486297616245185739116228913473000321724069 +3402413948021099348085449161508115950805641814124236561124067595528674925736958070838477983849773356429271615279624883406948406 +2586362309829746055325863292755259087415528960697154410026073785183365372343620398551768197389974234910915413966255239135166137 +6948141006945340339297784658800718813009858499271702100629421441689482806248257882748211373263608811093415305500022007093455945 +0842258683716404942736428616895358695656655266518974938995151040768012671515151979466768515865903424648012383400333040320383895 +5049269512019956300105561497239010604854186206683961343893764070138734690927147819767835829400814779254514025647466904224213470 +9569948375696342652641662102284605174592869950745185014008552719701570802715911338264166097617707320465336842190177333964575034 +9428680684767284183633020532362541285325879314896297017757679938122092115670160403478099811406505218708402491047782860144041267 +2048668363875460097326211666406188941409324970507852534507916458181293600129195542233794471499710031501087442970014489278431811 +3405993437193068750979763065800204417948705854493241428660974718747020128492308645680153785966021095932035295060527677641892559 +2814589083509652662902787080397228682431174828014560598681492336798258054680671382857806718386021239491601389601875295478518945 +5986699496572944083337074930929834075993621216590713141225309670719782429561351058677320246208472110574056384475102009208825158 +3701143845992716374497567663523592738178836225177899343548036997338523733500001300643656167477000925420983129066143726434092701 +7423291379038754262185514882391794604313384501082737513845672893062736061561920457702887522180551612186897189287428790772663945 +8038882462960387008399150903931548842040303337569963977574310341965170156166264463639669524288945874943197641758694114087360524 +6755427287648373311476510042437767363082433235534497669444705172475485394287772438817148371693824101932257743465051113814804574 +3302584529244780057393036854445554623243089576362550605055694156880682183315829897197741778897975736012282052319045886108088033 +8087176493289867561968800923375092924482419715512093174473694625106772042668885277666098646361898523510402733964872800445131037 +8320007995921770348498605128869111626930267035943426816435809551556518111290098800524644244409358410267473862764842446130048523 +1436098828025610017328979458386153232076446194265248731690143382189291992244935737904603822794261840119592775301158595671929956 +1859896675014716704798696047818739852245706249557223427847334266658566082156593745460337399765187932861285602937611319183698837 +8695387218535720582564065835479455701900416698839376472323627804365945785468346621682197696163783828936362978547295808542269445 +8279752484506847142094417985544033613129823835703913986703044915444548737906032583690923865665220094573089105884291792454719678 +1316169725449877686068579181522410565098719766530274437681078973112896904796812316498589687674134438686508004774642199962324204 +4690063619499878869524791692206588255254568407388459022366711747832387099104508881401463078787110967721065394141648606132537550 +1451429470869671002057789223158518659342906175930490990163086609677315590447420960904216338864582531646704745882645257996753003 +3594769224483393076962577578336775804994360716705648101110515582304099946203974654110102114903083901640907564773113403226112614 +7842811390980512417623967516307862595714647209118659868698680060437058188686064362842966029195804418885454481156401460457680810 +1353730331240432284758809177395482784888827256712163440645440749728289433010840369051942073911490213001477219496896397735829211 +4601945158211709423733169616586120219445506544455684681994448828365740758425856347182450418912396580393258871768216932273389644 +7546606883962070377792485250777570864737360035965255716590780907370097846008363755114568550428189781279182714576817370377989913 +5909871699164748481388103916215854782263584875652627145927520783000462057342075067627629231607264735052014948668522278967261465 +7789853062938105623444363261994088544665616630390123445507860288440231593034911635167838522597643333127458708364830418263896184 +1286144438352596484065325500754944308173042437315362632781599996019422261591416810474894175619846059414717720198222378370653571 +6971462477582470633206125394932199429053380382783796898399516803651438673139498531736021776634684941580638746309619120992683170 +8629432822993724963855361987882118654589234693568266212440633573606219295689682039033133123609808063033145394447533709295411896 +0281594967652261579794466637274919984182923757681634332365968310833409307348568763974412665592870158544818035335013012421932763 +7655826860692715418764005666669293766929001138134358352328235302135813053504743107776878655717896623753934484760219931500298692 +9290168663678775972562133999711785783179036668870829240738642007250789936063529444287722257668245891843786516368999364205453698 +1388094089899362163718773572230079214466119375080348978705038480095898940916060131319234067470176587422484787467679366538347868 +8017293664803927630769729168954945146012294840940686008911896485848777037133106837925465110245874730110174759001141830380040038 +0120439722450454617928179479243857705288134086458602893871161972846537382451740227055242495699985657918659780373398590814158015 +9185549346791665800883510913273745072991047690371888044204398878541170797025220849684634548447569944177523688037116487344544844 +3983330147768673814424552255794031561316294164196820680509267685360230979960997521617397764375675161021883986966830273621436840 +5559819786199323213875219658620019627595340113429135282821726501703289978583492131096279542835050405087347140325687712807079487 +9787946090092230362250195573096660493646419587912974580035697132589473417918325240116393969464922906025196729087774385693764553 +9860355320714920060965670358032280168554148297437945180860387882403591842300282814921233640661490871801402207762626482522032047 +3097025361491117451005377278198487212237099544908247111077372764356886263791448989161160949762415522639831639294698581458345985 +7119461403366321455704765813404706513096376229662175435103856904757065360840168635845572631764671801632765978886309611217770123 +2355725108730791813146700353437818727398062989969964100944171032459233591148110740093249920429111285758187817891195538479372333 +7717147589069034365041184332554553609756818941590171329038369697464604453126540943732615622700321916184110353125350100127752260 +5400719243883713007355805618610788516934976237500923767706894033322172234355015854525038569883308926393709084041999484656996753 +1283666031444516796129629651038339271512674202354596553588525167158551065999313761923723647718179391480101442800341777895133996 +0753973775383179955400385390016380992296547654995782315767306424246762945687075752145691419815405005932537449947304486849576643 +2979180958396224767297581462043320034127704743702992357878029772922817116999851784773690688789215232089766369127969016032601856 +3851573261415488209314332793330099314068046066211719044146103127781354079770099732602779302176734228288014045681315375260376275 +5613084622744138175415292879907528314309988380989805830104342547726499059288400078772029464667932988421246953056079406071204771 +4586730576036740853854978952480253788859203002939257757392716256983049436420156853771221502897572109716517239674204969203181474 +0982119350191052441755943123327258611832950316806139591389360751863567226397368531779203766930988225110737707276225086269268083 +1166609927459152345242074308503846915870742513191815496448329154263550697382001973332037653901875197894977220232463712982961473 +8742250538546251559619529217497793362557849446641056066254726761640756859174902719692841027963345440941200289292587371677456563 +1787473730619074666648808301286212846168364513813858291223488226257956569272198811926719725754005926219281926579718279777830444 +9964885779070155802556863992597887379404305287991864566420006473195804170299129414259096081893003248613986369776375193541122827 +8679261717535638702784929128089835277682571491318590535314932208130153018789998592505582846756436233025096155853923159667457622 +0064101062279216730164192200983393569735835140706754353527254528463606619322108164129797836707319607352924077829068257856221565 +4098966914598730293864163204728437998349124344700052324246741119119244973881766239805489297351925527481613098094358252765642656 +3256631812453399632987567720287450907847236484423613740997675264597104916297183865029408528402827703543717537820987540612308827 +9763193844988062420108359297687488837148328286540087218373263127565810612066788312087172343604520200144217517124505185828378183 +0839169941798971803467138716750724876136916222233258526467257984594030393990037357389464526262272705365500201964650768865692587 +6552250604997641261294087071350242489522328429130222914193744223497369981001341679952214443941383946458063259049876349424028513 +2213974436158368636025109248174911312580051245505113315795959437260475388392500593400331863318219524296806654096778830779216940 +4878685599516319690797457606707664358483790224825508827231099632903629838432588023623129489722939201221165328880348763488094404 +4425772854689128756512183299147748549960529805463272504802739617344094375960651486501133996216955411590283887952698362709773340 +3400068591062085249345178361745959828363146278918068111168494145377017234716441154409189698261322125169944980545699464089750536 +8846865398199885991570109728269877331973845988335052515573008133676441111176443077214835337189028637762538757833593083011292020 +5146028640720658045073881058022137119722140728994381646856170548927402303281330627742305686994941764560406751703346767750713886 +7209258975092391507951817856714755923161856176501059139093203502102911025009832983122251947751287786516716014787221574638939756 +4031047248735708744857275535082996528246946792080390060989384866585501142477546435169883331570932498501313613151748523824910672 +5636536308963660510951867259797466984603129916969819578084787382312187680895777499152465628258228314680156051454804934741689251 +0512085879302345402847429395015269085320583560263939898103230391420972595529857458723736782481282792113866713373594735837816637 +7989131244460061235927200538684991689192443600011394251966967305334141431951393722271395226793607939272662478632186079903315157 +4105861225129127048237596511466748710669457275423951266966241892139141936555720690437418563605034436351637331842274069357984239 +7714780490538701008702866992517751463558344819912101444037343071198346617249265664311758101747127784897043351442589412949637554 +0794058900554049165331519254294841158439399536633260010261808293322453935679510287405960977887161196498223064393352481611148599 +1300801876528225731545729262391681879452425698425569733040068970789105907778300649408653356394071182530765139660697696280006682 +9500403460028711452841481826687628011337250118712797471026118134978473459279189369396543791538899643830373887355243212938105191 +0873118171200026341001915337132116843336126642682077009436271005094613931653248785836179383736770204896797782028704969977499350 +0015763840495758341501065058600781266869591393444306380925176998016954972799133300711081355008885083487358653288976991291458433 +1182038281534129680784410839850542254891681906855217404657576532842299794168911463577129652659153953666202320168343676218151354 +4489515205537483086890747806185147028159038841561163491305751443503026033718219551299657596422184541173585136713789948319572208 +5427049518844513938239013971033113865769356316346953363872557559769111015332419532829697997722648255367995610629118024243751562 +2407474388820183495631279695253839995841338363502759887550630065930650395810552864219034911688436057782562693639597408091177372 +6597246835826333390472023469934087393575305915386587464255626374910540525559513620612895257369286541372428722814356277369790926 +0540482329932732167361734686964636330198342028336626252732092257904785101659859937597352536082164206650257784256989122547150860 +6958569549047812907458129176134743095874743438183862052347741885494624143736509130890665487847350948675120079666371405006768737 +8780431675373547960195603385932928721012510177772630104840609721475744144915357267790854338213160647630650329373129575638594956 +9496521524390891252020171500116808958864975665633521384560269916937417268750283135245520953900937858745308291830904258244389317 +0015367958131832930196538448945414897381505579147872992058659675192664335486653438990141847260783598099119731774723267313420147 +7668586967951300028516829535113315808229408687973080784960696295388250987543953394689424360452251573606982659417899250410280988 +0855610169950498351316129660470090096789046559977941298373823170600445713232790694833343261118257499105417238723309775430866295 +2330553865870284141777533356762036585595078938692432368606760459748846645365953536258322870084801797022785593339163737467833147 +6997226216417469287126591633487052682393389785830401431520389186301156111690874765557617527284900760279233718743071166517225051 +9560345000426728260399841368953209835209100487337639538936954570820031680886090876109665021023200759147113373797389077298422122 +8175155068276335352679806117326566700814346009488428499581701401590558929437455938924635624938224553855781741120788865291783407 +2295536946679503673362429092591476438921243024065802176610048872694278802256805275897901963915809617356852239830206442834187860 +1980560976965565165140981488552352620248220002599581191144636050647934285203553865821505084561426414945738261567792216102403148 +1151908591260497769379834919810177114427370448525005415075150280396616811812913926897078347185577604045023394052213732465977204 +5152624009308747654657296667409633669797187558140762249677298229159695566185953786763883703575683368256625993695568514289670471 +5898396239237604417219674285036697981388707533386023577734556605753254850219222927862511398982564982295186931657039590148578018 +5932796785525089331274908323688642021701636412080489905006446995970747450915290182470813258134915840332610387871533886024451368 +8138194206803987511384906150527820013265474338857553899093509868964543274546783521373587071739152356855552276377502435950664343 +6671221923018976396144288089085545597962326938710922261699996323322275457923099268605292778087753199123847741515682942101433343 +0553836973054122354894964985734607674790792609106461042226839499946875666977767744836458472114784382708675816804952000351499560 +2364997010884637465860014885474868656892220025469289156670808481103243684995557245150848863996066555128792257705475749161690025 +4110649532154387762250909130921439734079690118528643911754202049214907597563788601359791270067268169269560624171237553348861595 +5940616224028249469894892261151683448506705471885912971357007128279832881479611563158245452847817654715394431167451576558923691 +2701950548115303007534807474569747157173002064876638265966317419898895957065042960548048125830083650783906003217529190210069108 +9448438509033254495362865744281724512323137198768884725254104680523096628653622761003126111128301647813853623032837646549389708 +8800961571279179907403520239170262557803310568213744508717356574441764473623277516011904000012404016704294869157929203333991614 +5621867976685621217499877201966531190154493625522363719922688125680349437187226891224242675375445798588375405091275415995993660 +6776602064451444697704536229609651834393352676778641614610712731028707328894803760218379598569050464723477953936124087430950917 +8609350069628812265374110081821823052163446196500492527833170210965201939797385205224718774402651526872691305286672629217382035 +8488697066441224751559631066164022076293716903983024132971083317225244640782734589625800094058649006568726351629980984900472989 +5845746441409288197669763941632849306888745439411555999404218596367981143977695990224865577494042079971012624349908586273448094 +3944731944003065116712174476397673714373918870846441482703374457072218866041336694655582361341131880499845103050687313792470342 +7857292237476195260600973001695477364449307421802939526518179464787823934178232688755800049682688090660718430541383983270572304 +0611341821464316878794523833266573224644012837504558841156871904475125652999503438286924058574554384509785291956022252381864357 +0405626679943651292212678664558286589975871490755359605369624889948784641951887207653741241486219388021477385980747033128784838 +0349563685997861866328510113314436119201124870929479544327167409917885140426561670911266619220856732660355093308773694213795936 +3842271417777258599324767675283259950186722960735488702869088413460362934496727774403713216022454808579752649987147082209915211 +7262086846799716572297263071380322488276591389406786696601706848938742607301811413241169596156442149745813064652698654676224030 +5774876540806221409456713575924968982273367206175097023050483195829231424480633395579433434537883182331511499750784779245974304 +6819745509357317439429415625378554567362388569070215909977624507836378437106698876357999443207523376055325643289588788312580731 +7833148656904363694334569121139758945904938915660261426660062808527488257575416983895078937523942074734502850315306918181450849 +0874641305830980547277712020017148651915548171250649493853510014278214375521776229277631115079740388430042785920214556466053488 +8302249778400205215684880334891997915871809162392886542987069548288428411471154533517912205528047987025944754683578880543791606 +0358320982227642668267885208147099122293592290139450664586369209811129197752858004803010616081626472750567559363814707696436535 +0313506752837594147209139665385131531998918025147449755897149885365821721038971422038604629058413601987368885444084132780675032 +8698373618661979252204221364880007737028947769589369624595251994724054095272484618585409406539000761630748495654887039290015310 +0800818173175869261167775704612174999230343952148568401102639665953327107535357309312915124706207238389207300777172747117401698 +0640073150863859675544943154734483059715378564796905159762945298307365280446171698542814284025032267504158534248530901828343035 +0873201504993233632733239057534034773053022444340692497950449652566396669145334447109018793398061041589027430348848568737315985 +0702523531880691374404054678759180464956226260133696133658291458560416403819468440250477647013266610552428012647058409477074911 +2679888054788444461424298005407130238905755507645631966764120317200939342102226493489457200513433158963725232826211305616759400 +6659241756332251314783750854468182797311328498121933543014291040088680657858265588241030117849346100683667795040402222839296842 +8277427249362157833641670896337597700968219737912856460330555628526942419632625557211562429343845027815601523792180592076379859 +9754404607631948362062894438912954500557072195072644550642458309860663622333701157823267362150209715197912528880882392701349340 +9392967484360182578733939528902161701930343748675665636284278981486199253820383071398887821347218338053979003089742707812914379 +2904235361284953393462862403685101898073830804299574939556200135991635904384199077467177409693740584708928799458835571980205648 +8121614185438088992376830836290018683875444583668400234525664787649173577729566744205001928280399848363250846307170382670891227 +1379156561645180509829419293369128655964653490776933715927876086576622995107120980971557236204092369803081979966524323872401105 +8210634652239640140303181717302977441319086010060056823202877048062527278336721358234059108992008083109338742139836036323492605 +7384031376893772770300054085703713950658765604751229388016547464754511995220292088356538057488534591958960218276518555883565725 +4074775956662864334941163174462487475635543748972688834049926463696503634025310758502751556398690346342648412837461668031647927 +6365750302288055740676475554487791055706610049861824646116595120976061102018119073494765991803213702717674747175954421813209939 +8009331937511328151001411154015547213063838109162601512214845406533014265983012161008264010599651427488015290161524354573353234 +6470978945561557225050621564665550894387519941185513668617316911095236397590508616848692919250154411755818493336504839734750031 +3525111855377922515708294426372763399105358667430235504503228627410198905334837234851992023849271193012460177555030458178964902 +2832205291216922852095126506355099210740893308816704635379794133382523761361826241501772732710458333973916837834880210080414250 +9084082447350749526849475397900892283815818083030975632291741303292925823304366055091504295072069505478412237935230243114270458 +8733240614953524515745727759843129756538792646920469274157284227528661937418719037781765126256474601006075476554791323844619684 +1035243868690685092085007530104395247063420667025726455865466535541087734271159372906562129287612463548495628710375386966180600 +5440542501876451157868698528285357586655663503645304099842630588650168915815059917415278238955617758631802903768822104984447821 +2018849475721498910886528030895133588236820109040257215930892013189827568406433756483461049360024443491773721292902184969255790 +8822875627013591731684908728396697952056849491038175568000507526035874582989907019189676297477355884544173196568562596540184740 +6207027832469439408465681825635058880584562540284002435337574271598044807397129809411607354965616450909115426381853724556891033 +7221658030059861817521753782741366902139395897501991336285575875528980544038829626149666345302799699624831576475243396464744984 +1649540253799052538081391692469181324705607910980425515252483873483980593877409055481936471747872491282539042199279780879543216 +4236145733824367590860331474571745015558798839670826761096891349398910243527132904379844441054007921880393265550231971508677926 +9325201276405157061556423958329163005949768823959483768715570931105074676418085399720398268434277889429566019457186074673651582 +1556755082109554008455021604947841187746491317217027281220528571996910808491743715098726471698066342041982149950618417860098084 +9612051527440619756444098523237719085720236857461176953239899550769256325996619384467815272576473677824512425753715748473823644 +3293706412332532029368150574309147409666660955800738909802362577371939197344269404239579009973609254902956047445459161013030585 +3889810572293351817285095560307955387570122899356894847953368306309169743185951491528192204482516204225431961214922057114411745 +2466537795850541088028557343809549480793529110899163252329393397207265339963882361084755962562912216879860134066671514387618346 +7860354854395803331508980945959835169783983572284248855654573657487835485311040917221652836888254950796354469715116733559514443 +9649687850645361629683896013084952406507914922578933911589068814423571506614639956792270631464418346423540206296752844238915237 +6020288012912630828121914201273847417936995938147436941121877369382021368921214557882494996867880498498027992143690818597132057 +7464732232055318247290946075477625177092427701352448918522702341636498386700884661390140882325113012278493389309349523351396213 +9696483275515491260944768055252019253491922403588385357522280969636691193420388846988546363998678336385777443993831313531774583 +6282522032399446565965739736752150396232120722412203505719106262036358824681398508892551567229346012725815333843066908465157756 +9407628149379572734413508114861082667244920525693627019900560538557779908157912341364139167913811711711544677292025506854245811 +3464634373606321375959043076084467134891991975403488809906777158185809859079955799214529934428440394715803296141747865907907489 +9423993889790762040142434803114305323935291193328498665927373291227929076575533913306444776443880643225751481031179680675214923 +3368470912062297700011980465345274304044785588310672874869214201316579834207228249128013243107286248089143869069547088156308777 +1414590222708121116814503748513196371955339806257298864789960510451079590735116383580469454857965360874314128844784304143454069 +8766678394720390254448078037404671513243310605018703312189431738938607517130277658972087080448195787216475306011123823302381254 +3391550184839202307004590614178501644771630600861096494601392733266531850599404613838770697571965897048614790913072319558856284 +7803230787138777594218452988887094396749088370117209351285801556001096168575241228759515079363186428923678980339871398190852582 +4019696628533985636279505759337532628472390302841917324608852947709111244515112700445738982371253756187679740389512024539739520 +4770847899134941839230668652175028834007538603739876886601634289336067458758592877494727624461743546957277223347887135182085175 +9704365765485920770156655545282270424007413902597806432869012806155483311798101235310454409760720811406614955012826140011450968 +0313480515097495573412839063893028999786645804567919951099814820194282264604422806064547777371073192487100289094441987847150568 +1657543112009039941765308947835121413396501421019711136903259340105124014460634621836811024214292779775980478293474663062122820 +9705767650372955367904858685510440746508331239334572782478185604370866609254504064506288513924921428844442806366648997093161941 +3489214963851233247024856358669087681654387218345765317192165484089902190489810844772030255183904855574220071570304690043795231 +2065091959221855386411960322245401320421129454685844655947550889456851989959823948526569728196088066488438899595441765150072821 +4429114819428348855107669954324003559235452502739679008927728613312163086830142833511895866448006257958165946765071157730382695 +9882679827690443780755577550740154748999710142655073573245474660654485200341800425388615819101330831466686148830980748666684090 +7163405135812565179670235235614927720441373285793867065291904861238349709700184831060873375476942470039510660809272264903320798 +4462917767939061046448018044214701368685457860623889616757211734619696932095108903120448131130054208365085745531785639497736359 +8218165839330532616994196742532527473553555966673141549749982047712150106818828620285541917373915693522694719282613944281399835 +8674485087809037160526520795860814268331241546826667372648249043039257847644579120849321232434718628926179993145231262359579173 +6402705380929919120941181769537062173912605142768108201761013755197467816367902585985810911153925853453654100594647121313245913 +0666872833826033632401371436548685693080139022484100159022033636174658329127466366162032910540421919446428986161784748017789598 +4723199613767942411247930612753447325541505390129994382585659648148388692327147276519250190560063905452973361339890280400124358 +6913416888336681958297148217827338479648390575317941960750631878321348332412104016113215430987661026879407929196484877377640061 +8494663204235300224243052398541725825482919073092074355975063032796596426605495082752541621103997044605964809112021627153615430 +1932408491193512375077001954464823146678231602554522550896013165127235920096414910813761330938337552089472955671775628068540474 +8216732019553272236739723100215131535386354969462704414412180804694918899617675023795044921436432605559415902678545425651517812 +8025247190590822148102225680479409002058106823871348717740963812585351667254545007307100968605826752214724522314039708032085185 +2898242245907570507615621864533672777989484514162394933849338212142607118088404798508269488444104500180113716320167923336719325 +6087711919535546884547342110103435389748847458344462232091767124995184518172878034781269231230242533893228603793308922587510107 +4591474660599204864799066811331150365409997858447042372594886218949250820247279474918230135657009456785941687614114216570961562 +0712145092385555182611993058740320634997625075753366666542910024626577807784857468873721417913984050547176339602792705731039726 +5904226911569176720780206777188612328120845124057780320649670149526594811111907198960857717925291216166167928639613716137663067 +4307416456452371611701931195073322788105756639763524372948315105493615715372579618838296610933712476520491005806495650421658587 +8328103991782890592563008949384942150246156275152343753139757736003563406755966669814359044466955928045317799518086337670918431 +0138083133435376046718367707916060720252112445257291714225747183751131216668718597844623190537189131972377334685833227229634752 +6238721382625606157004023423898943602807707672999152909288657052128785368828226605301253816178238566671806542598902348311422328 +8320139586923410723937149546492213794676826048701166669784481756530412597915193651538414740148097847037639468257092287852640305 +5479022830982203377478476331811758576901670233527290215872232363518816501458927070562308753377018843087767143244104321568604135 +7369186061703793181459759961931363593408835426106781211446368072605366366571248851597783995945722926222426820630491316650527779 +8828573417201809585216027386210261861298732060253123609864528252840272478045882739663756003736716392199489082689850824432511971 +8111646983909651153366476446390221276039522740932580204249319217339578537772918571217444956420257260371077236650618481206170288 +2935054551970379324008798030984106801055948226939968902171687217907155340893582643366397894579369212912093597160438593641683669 +2485378257568528389277310889609277386833678172566456312002344737328227016110453362502908568225831332260414575845627976066579941 +3482852162228716106007258452673841400279159876124086723321209861347673279139378292982201754962295388660515249705755465339980807 +8586012495756212505119149505421333725699491270326513988475156468533804482583378661782959257850441126447111003717332538997317723 +1776086627675735007130894098661445494477133237650830139159997474669449285984774365441233470966013545796673243563388041724650925 +4871204375981896728514583939521811481161849278118060344334503543763509824336823270998963842004711517359429526921820434423765506 +4949119417418804810189631493303008698556136954410752013975187350631734375598695358460132019129899037320268439555839944615572946 +2352205983666568424098911534581381260867463768189900460175744875437666192115066390019086651996365032553554531097825841729484867 +6396761821871154925250940721334814793716957966097731930384514803099552848004844714015522077211735687647117734547230837853524693 +1596031648179998000220637673943792334209148422484797868537888852196932416293373918035158752836265069893009384789762509582269339 +9695024706050496247098862490788952376578622083442656866053965079277898118236124486093149364786379996025483226916387856904200464 +5207178534801739733741859262250257717063345689422848102283865760573174574242830519134626672907219548945944321186449133371889496 +8091522599148708961675484538617588113615426169855145369191110338586586013267131819054886276010519517578355159744969858769754154 +1409888192657898889323633959345553144675637666158270953101126274823625732454329591205013981131246606853736622675628645701807371 +9520905296639004266979383969455080705361676763621326243504360544161563837353776252465278766865244727756979870759572455920688699 +9581386171160687133121725147597447299189214610587624098233087649334093755829602691095533964193964555227483958747057992036054166 +8721053344317784071933338712749304229416314171955616978192540365793299631623144229526476496982594982691261090976385827573975900 +7821592310703338108652726889016577851582851275588755649801212540201927028394346407971389167170778763825909054893860501536766294 +1680216758688189041294684581042122535978201496230771039365667061380634389195094311357702877874169983055267901138828354564408227 +3161070157288336465271541894601581266294703722546655378330486847268915166615856440605517140340676687703279431660743947236511822 +7741994422834194190860106225475557703581613419594307564273066500384493526007784089212735294217591917854424698645245431154845570 +7453435530259083182485810144347944810673614929556646679467919668579911140259730381818980942598880746925296760172341499274790409 +2328005309406832991426090784212693827689769275846169654962563240745452602435209585710235753460014401490458336687380630135023071 +5366592739312999644456849006548399917819901894358497737731098338943033079178655254808256654806548883980302404456845406302737523 +7903862311453131512315977712627346676478344399846058399879029101474967706599628983635802084695117268292559579897221003598626395 +1226042367447825280036345293351132154926212820976803484997329381495188404459278925205516668444497544901886948777923893344531981 +6856828147900151315527595804653628550631963940129883617356848115429626055627960941822258407367220366681493979858804129725050531 +5342427449777364357294403483728929672993293331804282077540029002773232860603723492768689779320843055290078442091489307669129440 +2646993961014124319764296539370434958358249722078956842849671467698536721940846395173191197670777284048312934901527571955362838 +0198542559113109732314309858412233715215167131228324026800689688315670022802995393900835564840785241774828835077665262843753444 +9550183762744591451958276893980778053478437092327149970098767098796188062615518630585477187237482484844353645352242801527565339 +4554133259430221270883589344804016824319680145988501368070988742027201586296359667866696516024912143872664381709357471736498210 +5093801098192097809224518282804541968783581829389873304181998214634779593308644761544892301474532546795113168811568716153209669 +1410860371073926432124196875939453572716037044352794395231284292433400177009767688979893244784882963302195811252949131002020348 +1963827673323077855623143768105648991600618303185458508264555147286917125868559346485583695956446151749324530132278047444739067 +6874456031627491486993438537612795741124963717273593326065380547768578196708046010574823669488232340781667689355939847002297664 +9059011156256654537948571823030159169978023672567790424242785131594034312912382632898662220373433122635586320057460971136026042 +4324851680908243245093083193503431558790765585803419091388697495752260988154385332510991556891584925155976792883412406678432776 +5169890219592441180988843355694784180780002217839745830787310928598719082559628331291213329365294364549008762394123896795514907 +4029304916499522070046006091292088087865888798317559803991873835903034439822636248876206336683085957754970026069257438125567024 +5122833726152490145361691490840338977240905109443464507374230617046450541939205551391476680636725777819582830317640677472291259 +8887879121383290774133008351057610071339235010888965861277804156037797773751958774639837524541250075316907358014661336099985243 +8039061153806899718406603277773464956989757949717475056088907096202984062415028201715624897091896396812284772715615886727140592 +0697572299245622972158982736674917157478112408603912440899066443377237594315710291599368603222944799827489902803052151032150505 +7013603271652485049620063445001242816749693945247727354086779497318820787447165373787134099987577277741625609459410605718145900 +0792184428093364028147126779227116462983472771162905140500152025984437549037622220978921623538498863508150200422542729027788614 +4411728159030287378714419332373354590292666051672346415137756916116829485503628039118411108736302251425180386282192968531233608 +3508034331771430567756401746605566004814192531598982097023747313496871602413228109505959309225451717487757635602112764447022631 +3428939513944422073187530514913804210377096901814387277664055693937327821063211837805670103207586535472844416128884599982345779 +3765013258236529201610750246167509469405857415384290132571908547691847116538830356937781623544344792657443940779980096189839626 +9024565602139835552337325332903285437411134785405743434839219379066510469685518924233636921521087402359934033983973385991471002 +2649847962568274167726165208946386788572121523811142222528040146116227215917720262618074588389904637062666528544746561556536935 +2620030538510199023964637151697713281829456055441798954059894139613970616409824903577033806155241943903044175681019965795785283 +6461188416880024087073825761424480883587286299792763357082205040327994699188993856940062456204962545880583868705703598889787721 +5944413986797134250694896844973795638537262177510549522817774683727110576630405084332924025735617088037709220742387561124513292 +1615973751057225002195236737059699517585264539200525680337988614003524501832730480276366257598203042140690301023569755795697899 +9008200222469601333665860661780983941015300488620597776980944258674129973552734738060885268066420909348372616142102733523433267 +5003598890783029733738430164315966147920008508177297711130783619606431201578483617231981134136392404255762575061631480099367673 +4714080230392459893569173674100327560113467502678424574866012722633975629063693151545366570327641280682622355521718987259238801 +4739385038824895235096103000674296240229268577109938375885209481939108319170129231324096236414336990925757528939018270228944966 +2361210030668060864499350273214477998565800871680472680681932947260121232176894643185754378295528202618772690219192407686919753 +5156059412217536746848726448056144396547837914584560922069735882550291341190213498634134528385275982127813806960692781201779053 +7966885142214723811725348168549783205309168240872140544601559983829401790243561220385975487819401267336194223205730538148703619 +2086985085185073545036414851529838204481235650803097024838796015231330542654233028002529608154776678617456164886732320375739646 +0833105952434512974899609076351921948939569136092110228925816092118172001373677700416412487399777738494320581470807407424704877 +8185614688665309617727123329966574828512583312557787490171669059879359795468437113583190694186011613831079902014081217754134725 +7131783601536200912167420913195076409694857630710552837087267662216383925992631278943851251589580445676689967886327863559646484 +9888310805734900531289699098784350167801387862535778038559729898236704661594447329313866535642213694637004811239940871137589762 +8123715241748226553144892581245052850529932097715696995932059624924557269075540247991049472713937530723957720713793716760718395 +6093034172460809629440737053209815131819638726936540787299401716655839213840485847609031890782549934060830350478366179240604434 +6633327894940509763972045245237235718323112502675490128278367355639972323817271838806709048068724517131782516093900913433491465 +5582353019666679291426183344951264862654895560952921265198755899160545875749490172299073507227437486882523588711701507976499572 +7489002650231525223980434094738594856401532028510616419177773329613390523750901433006132084090755184848290450781826037290985462 +0539323854296447671459242043430937704073386171637825236119340677027183609466820564020370547139050173937355911835070350187485451 +6965582154750741810065288181652685394941853476132562873871859640024361243300296805283748325817168878484565585632754351908511680 +2886210467566660836736517150078351812871682359935951946868379376912994012664524203941072154686353570579681603704825530456774626 +9665362335544365210262820037867343620764616695075643840510926473275049084133707571284177399685539645626628023165977084111958392 +0177820463043321585863992470028736425683626513381632161117010851699245594406701811450201210813697392240877275493182279748367019 +7250981623715632368538564967386387747576695271655268440119068242579103997269027655460321699463398768768277613166901925556040578 +2730921762116366900155596821064099866827590787028023864778747307107976624095851829841563684215591447795700215410952638220148923 +8642501753068607515960160175361362264643558443610150876742595480796702902130477857992388072241713968623856298400849938588555032 +1187308644994319305723690746675187962892871980887310999328792568364489164541385679772588717510375815217942384186220430941913143 +7605854751867100384861067666196612302899049367936234596159545293447341885894031370313641111815396044152641244256403303821921174 +8006560725678932454993887223171401960145271946857211912769628951507047840598467703395747670926287903550867839502877316151413748 +5747061868485506527664044246509977629632520838572005928929416857628593497937173916677705472302579778727680532363758751491026938 +3023153622006915734476058970896478596388304728569523091701672491484313935627044876208043141561946195882862365574965158147452289 +8540852691422877019070250316861163162391778272197273383387116022030059687803664857766804536240988573448290819394431972776651275 +9021443311810736715901066501558981319880316837771114344162148017989800388690774710489918382047318311988709068050500771418272212 +7599513906333939122554708100671399521304710290565461919067937465641955962609388316053120501043224440969693966990639899533003437 +3190313864768728595063193176469208637792413542897116168001394022157595453909893711585707042678368387735204900683732630413423309 +6043003917729256108805619361421477310316625655437814740309514638761344462517487426501249646525209961899684542352037279524879981 +6813035487710494491438894246146083397270552071559892438473910394670225142289451782904407545380437720612171136664498766583513111 +5704425501897608539934816199146825064755643795555188152948142674213138361295684789361927194878162187162220359319096077044697338 +1170042273866788055113160203247185045523345121557337349823787766288071325865947069357672811750151752362658390610542086745537803 +0182650295086344086825098102966797093995388649163271139864646408250278380402342942834021881070263294572807189069134900984272374 +0906668841731928141540118014775032666661994283184008007242068962044782267000273516374045366478779586459658472979356032532424400 +9941559950477797261120283165043361871515058911345852440301289987592080477208449029732297262652194811701835630896804400526302181 +9586422168434769367306983212903836891536506488360440148109923788291834492143450307170521991049310812325101209435949795579328604 +8845618327364168114007785559410459235882830447965260478796725064238526247677648804067848274177910485949526784938820751326532317 +2508842138180368207641530038974478572707059622379262380563191970615374835570345785749767510398007047615953484581417608147080414 +6150921916969845997446844026937752931358496152526223613349123080269156525204842351030887676736715854630790139383298746726376238 +0031087628812333418550306049252849828620365883770156880965318518371047868891562748685920081560624079882621440888924178323074068 +8556547813934656680983629186991073869359264408784267940961601835440468005712703124932656473056207782384291231611143830784642169 +4764006946356475094019331519107580885428818699148255825445877812529420751612646315832517900095651843319599421976422128038327435 +6209251031135205676846184796239439073588798115331056299413787815047447544760251266043212878984345103105431840273907195809002871 +3654931993884850780377252970969582655676559449815451929264323578834644743068604576736541418957979028943061560532360261664725787 +1145330876015831126155307819990268909839073491920596329763420311698726840745194869089228905040694957988171983477254803375572985 +8257437969668449439729079801079379205727147223003815670652927114347162605507272520770488283403449339977650008075057150876981627 +8526350745722665602676982911174850303165259851752432760991481586280102205346134401601648981884690319798225175260769925489404224 +4638271898447020123724423300538195550321048052825314720561544239062181964769986570926083174181155377779882057584541395809301918 +6106817249634524848855511452769182264081765291797239837907787970553958190403210902425419707216288535844322802242224844682625560 +0987012833422099095295557450557916850795807069003361331869709388270541973078686998103429124404642889230735965872322730527661759 +0637705394090134454432592452488278594036085049343720666733736875565892704518648963039960363828210137149300479545174722900885474 +0438909745910598389431226270312843755719991811348892485728211852221096700168476622881940496574796916264512821293932878797623865 +5429545451401418793893535526425687987849470147455674595433976709030225358630706349020692453458568350635883987698939555558472630 +1147145098629672646090540159437091576295858909288901226882948677153091610088843747423989951237804279634707873305141146001310035 +1453067258355914230711608884942303119909820139748995292741299997309447939834224950618749891034712331269306844559292626045946290 +0350632260797112269078999525007760708741928859358707432624911128337604559525225243942115958112015615876335010570860522898738781 +1402978628312279477469329446037732401120697968328000813953667479029085290308524043626235410022744559135665074491331563473370347 +9337757755044278436081677756600946862635252212939271849678018763209718297572192696470355304192247185638719953361091249281203364 +5405880207322161294391822123973103190408090878564615457135856917654118957703067143223083955082019279925860379183853717628861577 +7799366841975673356259200026023839542055869990618462031135739235438635258865047764693545067810235010528954960166478294307447628 +2027467028086256924839310527875817129214597755080392073496545377860632428440004957401492609606750537328284161904594401425287572 +0946928415702129545673169191304792948477302426781915513863540380482590408165885780307409257801864775225156808756934724074392504 +6885597618942836774284752567772379904321306380471869189416734595292130350179577547119957964477389742383246612250778027835962842 +8375378564184742787926400597138354486910116541106048723078444753508902260690644823241797242756355368838174606975331846084479482 +3212968824505986851162688090961261899834237009593445525206396148505570975743205626589129717163778397732428341839974826649188402 +6250784433605236331523177619946160387836454523224441645894992796160653797716355919979290100324224314406791061386652716371890152 +4400989435392530316607884601954369502551234892963245103254821300716613610559994576020035792556553491043544497572846045958494663 +5437689141597501417826982456539318735177994543195003033035189307876601341936559142966278723819207128349590261078232012690448383 +2240565515565951781099043148895044873938264025267367187036427905278273314523689584565134739736805096906657362307932608184624022 +3576329018410785775670826940638079688618282322990520892260090365771287681416891560096002562633570097542699787911417555916230717 +2453291822907103982064112069025122300625453433920178638265846617355201994938801382944512864303440296743023682910689965580508346 +3275346241221900813564939920509014369583021411699796257800366335780055834288641882272217012847000156622653258224710526325808198 +1761823047477639207496107226016946214853880512328754599996142453344143869395811453079561049784458849323538032761380820785136205 +2258773098932947134988484220635633194802789720564239663961212480883691013756304617477696306260192353528126108628749643695576613 +6833244981505084786837439890389565039428783638198451342774797405426073205683183254178026538685346206428799444442723636136435573 +9080842459252171303872018490896413638818633597602353810855257937546412235233854346618680040369752485990328489156699457496819504 +5734956183309699603857071255358660306282081929816419207249476965467352584050195709593369245076183392383809049796820142290983389 +0663950400843838925251438784404925348605007257769726864605575147487057019983649102686292523010863667700171976798290888401737766 +3133554911232814826547253321321519762370231796859406653514455605268584403622162920523841843001241629651167179835787795168834729 +9915875102181929094696545619337694209374642713183714372607433054204210229685880508097695696888633628128273726076768020385810595 +2987095117021662238907432053186678763052465613665295736124815230360572136345177452861949998291445717212884676001873754746660396 +2589155650613427623626563286829473395485125944790254164155482476888221798281733050713864337019212875437294907722695910181965531 +8195108479458493218118347693154287549401864008165971140576262020179031350059154171267408221692360825699802778787719114529741055 +2994227854778559135194271668156980433699910463754412118741685322605625614010496732911474348010716497147621915462772990056635740 +9885103937597375812527288674502022238204092589207503781840088970784707532373479354579114899440649532568358342845263798451171893 +1120400390759648596081862269355864764100400822617213332109125827310532445408926542692499006220714769552811438530408840934868695 +9637352403752939344901814991765254063329438135234927589317161742716459240954818619945185528105201605559036407476820639345279573 +0967725725520221343652248725994878515580037117626550685849684690414565200284452932891511103030564811419601323232328459974123969 +5151667166514928638097099027082126182546856178488195923185506331245117707219875304117100373941938587261574361349595466933901648 +3122056019614029244775838489907181693402454194031630939340884628769127341314464687448572665248434305566402372170442289658220344 +7261444332642187051262725693834547125915901896179000263992637439997458766108204237403853459573909864356641282495892771557042023 +6977452997091657310159593635773017261586291897746068707373152247429150821576531204588421265308077483457611680883539505380780214 +3922860418447742388907173493206552834918162458673611316176697519544920126038326376534410690399328547067085820148163398020288911 +4131844303372153660043567296613439825919900174540571926796003444468128320273120297033737036976412016725245066800816469650561843 +0565592489986925533018758592782811877964316768421296500160427160146511217146432195095933744734673635804950107620352420106654863 +6645406729317349776240373018557665618972564221260681309297356903494539710063100566543717860365197783559220679304253096607594218 +8395581134093404449211217585032937525052183907591091053649490577579801329664352206258963567937765723904612138153463659172391420 +2858367788679710677058155486700833423902690944634848842142528244040019800469662248189870722506296487688337321695524703236140988 +9562335938642263288699025637860876864417622879634601222106055965060418025394207515941236354344664880329773638937529679471456359 +3869629127425084307826072099872319475018151572502025420028453624372211412720817771154291359747053407476074728284771551844057529 +1190185706672366345077026144989614660307994733718658435474012628808621940611349704306495070342196599139286091306151010932970462 +9675509101851273861946201556462769919002921112388488480221448504521451553534147109710141587683279463216951321945573166307639217 +6672697005271871616738575015318763914957713525828413258526916822948066768551357315714715932196912191677461856418144576263544333 +4907150429612203297659855554908990742757537394357200499303314336795187732766056993269698914296065846201908473023132752908744956 +6150083396086150456609926582054844276634569272470435640456573226419822868710777542210573165448531019738194685407995076708226492 +2678786284405544977160356904372320894983311525398796027674382539921414318300162458602130818377816924569056780073177178248997811 +1585303840969268907598807940355932272770894001504038659615940425154386157294260258784536698627961267225191113300660353374029144 +4866449090853757246935294981633041248861600653432843768265539085865624263955878975774708775312072236190746445124355521953038339 +0619885412382828558999175920193016334591402367369763220140054897008235791975355396141026004273210423619062237213937060537525842 +8976484958039104695613723977251336268561215466702146950426183444969792826975925369611735319989524615202694816932040830397174311 +9564951821673710247264553143623225237946783695563652359502341538660577772441234930760198015851060420035149363908475167289811549 +6152784468367404157734119802017549287296220448855141063864170092034156049229526992205564211402819767239639089903951267480899195 +1084349314003385023967647400823528491190700765335630550586981662116778098851626565057148602128362406840285312235675524872617927 +5940674859942896006453306875133525569054425493267698876245403823203215376527985578976643008721792339745957322665402353157011446 +1029563432263058113381593903721587363928336506888785654392441615160711392515188910321841843403492466442469739247826795396883496 +7172639941661127457144015058984534581536740542737103665437103773621233103380626067034160263026558390443198760839727420004712631 +9914873013707039718441199896815680469326923849342274027255430706708553663676894461369886145860075954707988128252410758852549672 +1551922530995012072301491677655182006822391780785787705916872826260151900149221019724939267128249948853451126842232399605725462 +6980483373104508461112547210954800274259585332622017453698311994963928004018795768275261593898964198667538969022507330787533788 +8940619208645823836214853361748146283829606466502798396069751370937123229891589445495370452889766339397264192786388083894708555 +7956574006856378561036342289467521536334236359576203113726730817643632648603038458281607027513534064039819509732524551295593096 +5939647576077639500586029620496592494701352335060993013958711501324112331829653462612315976140447947573865479895456805542744172 +8296423959232512165579460974983453693457497491285869113279013506974543084139634238126476293873284263151589338438860044240330996 +3782355276288558083473518137834514003242038994143031796041577911531844210313799581374155317783854201414730465792201716701206550 +7105406465035531748842630956573813909293101829709167100331101814849787778408887961527614275809837033921260676349397115972110690 +9643615107680266865840715334731751157919061855580521203957602157773380969437003086918579100512932764454793288187949797876169125 +3320437252833110321043772044039248321368660878095814104499448998676046985620748260108371162632325229365624907230743947154301806 +3552088981131531046172808385452694435204741530370783735986679874877536428004908486014008409001068550950540888673128998784481805 +7440503329453373467657006789991046671852958451887155184801369224781302834630349870843861530093008719949904514151022786314021716 +2709390621195321800279652002200952192418979261420927427122958490897500556999549527116421814590121223682055231817153659082402135 +8861992826208042869383343763880314283402643395875744203109296333659792215641368608715044344315779183133340656043459727198349780 +2734853139703811794155039071217208536677070354393864907407146911703003457454092806351920281851834356124440898064979131802059323 +1762234053491518617088128044036706357776327020285634889111113700954005526394521483443227952188980642686375398780347144135711592 +4027397751732022026309364751091606371427768246571192961054471672221120559486812200168063013361795376094462318094221786026438363 +0392038021743121561693528064882454352142444617962196680006981383051817202169379782864384242280286939946138589599414498933359730 +0182070858522164858867574874421689046928982559988292189961850204180007351631664423841067686122468503764793368632417463823286265 +0700191191614031195459835157848968083002627564348814468750050055060620960050671214092145749062318287857521361265309131257106421 +0309993332034066779054757057884677047198784197094514566927373158826040214788501636945490789604082561095662632988383214277496388 +8146430655840989789550968117320096504739356599535135443521661785643633703575650679430439992282453217709233862686803968241138826 +7498984628272507825242938050646337866580174070972879570659031107873223367860364402212183428942145402115290592836227443279445979 +6978276114539892419020829624768340474210713091442435775100177879638010349673639613552216581396012901447719087628078745824194120 +8522095336564609176473140966826492093656451125709888847406519188859548795021576488451306844892007565711047730272408121668647900 +8524767921694471514347227180326839537161579546977834075174921433329341884828393393398191832645952423890037661848882505051151043 +2944580067530192277404349067174032715610611074091485944569506105415953050308842454649185132382815370040650599793085560858695708 +1141032332340176333446374984947231196716478639118202128308413932621313966803046203929215874583358851861354789012133869833074749 +9480985593249065406361871874582636508161948209786505592954264261087303579799611153927773325602584185048591266936288024303976476 +7322835259534879703369663910307964057121123765236316839734559189401161415410496300232441653039670294405632163031483814775087095 +7887660928641822340331849719497888363217187995951806289777062854449559129016492535377000453799053164705981391283757823729876400 +6020813393115291018308864451756852473844549771230553274019875173525511163873500444811237248088951714603052752360262642567865853 +6201967072308522428055969416019910663232530141827188975690227560728650829855364963013282370083361622353100870401742620986225707 +2869802657723987122733883328526226736949680510879014974422147235169606191520933028844627505009715152702759247026991603640320467 +4241131697918560404418795016499808486429648304834823604182336854359989655705275261891160035233666029073733408580774645759107173 +6464198087662041784095710258475229347049766003901523983606673438365749432588484287371422883288614656304912471987516429205303532 +2029089639113957845701115097046043607676790299218866434022020078360708383436488957906770062559366765186955585351813372430895635 +6464504289920784076224333417714908863182256990967502428628619515039237321418833968508382684390570422398059844256244914931448759 +6529784435502409492045275382927262616866859838773431100600230894559679830532230924353989524323228472158682810144077054469603044 +1378563204070129032409938042343154208174485755861395799608824865872543930138962034925792279431758568493941395394754047686715676 +8867499345143102872671197738475892292486346568581939250404454964808538362896520373387072413955104371842316500056740851235207639 +2274346030168140429762079586632175197653839654847793978202115595048824365507532766797058257757822432716594523867292474757570615 +8116418268033688475580223253494948603537184329744599083311334953737050247127839879240255833527455107807664847505065047983484396 +2629458032033571400314592548790506246485500676837502841162113579334258598171571921210523629971864377357109335285657941548264619 +0524276024669364832317266608416143775893545796330992023551256594493130557067547024156422646526989759981543218213803553279470269 +7049845656048970965053022592938828183548869262256486091586477018991837255857719144475359084653581143521408001578287580561915936 +9529610539570129132154472887914287431871889778678946943105713104741010716248000242330469610192173796870030679714772937881344366 +0545264556901297919417188117578751509627881737016436472816406518703771084849520268761391513510275790321817000658619656930708957 +1126630442325657535785952978581421903165028332745161812379117711348541046986438165712940213350699237254925482467553006850919218 +5721411832634196862603413999653849373180502518946059378586343176647830400545625593156677745062236816423794840646924829283523696 +9566889818345214089079695756528442838502922581513940730675014653702918250796815796276266282500947845827720283326159797147319082 +9317560741024728849503640154852784294588264197172188120649331980986731376355567665746330692183280397742589194244588569542845631 +3577828329965372354724624706136258680035391223875847298805321085928193394693686066077198681054782793606636920380474314204706128 +3316822771568615892652610314018713244424614540516258058981790879447066097870072989300359291800915299516058342141748356513095635 +5061676595203394978368391202469283356586340799916518917181619890218937093078654617621002548811013022192072875054917226111499567 +2293159175827100175340065081112035701753220885751903235819929349733241991099088182499008327172200193308932710369190557345577793 +7287877879088510408061450710486988984638957628372307246581657037353807145126249736162916823230146250853818942298633104586163230 +7832024984477944259180948598164959684871867948848537597567339850852013498502229835859692193114794153895539003144519694217709429 +8852158774260606249546637223424939759148603134570091996953512424441839401422606873114052812717989308319825300637980593162367504 +4443868737007280155976070920335648272365458102007219456742917706885599061474160397874769568586074099945991122357965710810304405 +6905794745598083595623675442819199614676323588131811003338385631872023890221085694977414242799825541355735846812034684719203047 +0457011399186690441251124301781521567240706876614307067167646882762698974983127872249705063133235031055532098528898796399551640 +9652581539551646223682272835407450811885069347498654352393131589816331647166209087054200913809824196442611900896852634745826036 +6159069172552131589074592658415752799244670288018595097465086916954714517572062029793973193555030320732773860687445556056374048 +5349001165930930982732148212936887040004758525708856986464739067246746909509060364496603927468208068059374896651091078566710130 +6254982141720413268677308357696628123925721390150502950572640806864637896561429156063059840807574455370484872143372214422469569 +2175489787129382942852202790241971823638922108920609980574885762787573196139571654120370722438096597478384547338469999448759583 +5779677161907195059713244890541696081649739347821725617254499816041573948157136918495996244811587574062062152794567408848302587 +2529817818204143798110200493373689828340608796471631850589769739013205825657411890682974212480864416875107897957894840172709126 +8663943826538718474564967927014748693701498556781723819074861406711142155938775092841806948205850959520646877840367557220520678 +7792447422465445177106699668289442956655097062959653482152033676820332098951332376110889269353539366684304599313150351375802284 +0121046685822850120961765570267263832452267770816505513149214924261156051159905321694021977473033018289683251782668439030324894 +8869657128937432079954235872959567283205921860119040052090516147170061627077989260167387744379514761600478703309504193576305611 +5381561915778733491108127139487324618000049052071735127115368151009776782599438434551832876965146125033956869670863387976299721 +0949362683385697791271115461446907297573048650262795825751967646029677499691298037462050809938530416641818484223928338242122429 +9766820839983470977275951789763019956067562051439591397901411073621068841669094149499180916451056538781312613407170881646764399 +6524347269748594984863393554929474287488214701192085748591350202439606866037583191335302568028741994749726846505035406608976877 +2807315518903493816008029815670675803410749049697801438415798820359073479308467800153777926573033244523898966885887408576598556 +2253435971693966005243248611373406144205306206628977772254035623423592393288603285157497164481644751059433660694427679833172764 +2802621839144263944769655055452576122669485060013276462140874654840615169333830556593045220363613387004343762888697682047116295 +6079893975072940528060570984236651746363761678873252309931727588216926947288076839743652207728678149523270862367475616321714661 +6924992946415404337508448250054422067066309322273329866764351438985216249046429085623842949432537700067268248539671410894502789 +8240333706088312915806196572306355577223650119535205051323962302240268709606481125217437426747720173549677961990418013172593155 +6781963827534077258276934309689853954408488415498885594714929736965951139633336078246485722224486449322009472875775669311469575 +9358007010281073437748629060353526682504603164238126641900438090676758229061250503441387135871208008336678965627603224246244049 +5356708068957856933330346985639541006539430131114293957097055267024756313531830324556261486697384925986987181884646073628504968 +6088781492829096627570053299366443750840538654491246720498646497885473443660031433479078885663560146413659899024021823265546612 +4698755327188359177076358836200498034389012684305157808211750883923932482111066086636126223845187785757048164121301047675382008 +4187475464845783395113764420886751702440050366103999318308171948006157647282740757274766917041519563322385269555023048099709528 +0571195389552617367126852668538451980401879674162973150705011024259186076369903231002763667511763488135226006543931654677750804 +0619275562362999032336486727097255819748979447931027384044606019902431591438640635467977566427621673095319535200747603235302696 +4867604449981342383768009732025809614182230646572537976384930951101533738349930256172344711040076994914315737263468451025930022 +9527865696943210023342715736597334765710291291516121893348495349788313734498145749015674214471321122949483123539062028956769641 +9765176523721839937410174251025970316488964877877371126040698433360574265827174483074565633308845572364434646203497713132542776 +7378651477723436227947997821691317672343016210083663443777377182287912898006288444888091959123188074922030160615537818420033213 +8447189492947088531974966589067358589089364485475991918412867217338548681050492469752464750298772894247076136639226285280041834 +5454360318746876235873485912565983134779435530640178745325450114100396790737296942281564432762776663339965033474759509011312252 +1273643557029602462258772262515086262035743506025601400592823850215645680844288819770466248021756708616474262140257830085882080 +3749316335624058049683001015861489646556394155367973498744773935515065243970160613827515488862189910777219950612111041830333142 +8451480666232257772267491027370674681436846933893722515862731717525412660342458091112720313220482317659721562206466780128741773 +6590252632454850440238194247507400433748088629955607174486085725118788995595732312569896819608944355538496441101640124949497789 +1257684894384392174484251124503854845960148691603301944875996738800826596693185375085047729958430171553740436704869195063748372 +5735178593590752060263331193146970071579446804835972630866651058153595857973419849597143669821080829676089777998056446703836684 +9360907310859244502541304513473974522087020370096397612021384703925636952795854616525882877878108950663963286923299046586524224 +4736748114460524678435616682271235781989815828869520783049700875658488695718736121600271316948975177628944650186309894116256839 +0122265134749874764508072046641714144031116931952372994620287753392883858943809994533079487667165865233880719017940897178306317 +7314139776044966808676160030164847624119585864721750026892048897546015469276703858536945215709271741054424782569701147500084232 +7758065860697872155705967293772173137656465001477901257876428931468366029449777184812467116641975319314916341175480998066511678 +8213787357873005347083763974187729721493820664650580516006337664286359010259386041876040802392491656717630783835571533641496104 +1539726264552952565436503783866624559510200362458151625173082533487696535010112807775556729174429917223668593210128877186508887 +8321470309679685999475535888469479559195982218922599887296055650137375133750432460874145869352229391171673429325865805622380834 +2056541359048531447828515960692963929079449940672170706409337197174760052966541653050946881379320800572898168412293068662723053 +6860373992960297640329729816572073853997874011764757398797944036292846733897735820446934726176024024757470895603256455220029335 +4569926110072668348278664329257941057438032693980297647417188505086076585913708391932628924011735966680243663432137597450444007 +4011230494392556854932236273332089389934747612548401261998294903567663125297736568382740395489945640372131150054623072337132335 +6844595318925439547616931848005923901307170686601327728283791660587722758968622818978597514280133292570491476960507397213932185 +3642313707941792113315508020910489195204450745180517492522873324890987317406946100562361363175345008151634614618218325839525506 +2427692898230648928178646301964171781644216637535917410571457842164951317391990752953058484915364895813865613835337731245388877 +7010431184482412816244666073504621100130866578971286677458525711336280431605171233723698610913008947422033976946085031938114793 +0877515916454772093488878527862660314336276524158489551235818954456016892617227185085470522620693855915519816376238143161430319 +8799443736799287358483938545063592502641553678820618531061987381595925735003006045937462381053795139314188939327005235192961861 +6759340733723316487592044207138087167698990178309899096979902164464364089126370344350744470572331313727851480014896363857798812 +7052560097987652474276867365814450520379120040581256992867022098643876725081745248682691582583629292763400638018100065591005268 +3106774405492401515494177672517042073837303505772295474776502959089196445416987270369786947855131616223965006450641692994332025 +5855582574286277020845503779828089079164761761910842413066849248818854888626393445367304058754822066188520015897444131019700263 +9198472044843711981048071735258326011544302082682349755642269651044020224003915379747598044212429909878394016016714106491656407 +7815377808589332772544290119723706266101079820396212755543706404343739006942558946534655221133089320660150834947988585978442816 +5845893139549225714132593150193123229669033684893833205775336633241518807901847649594321897137880458298454663559120194776464523 +0400879116612606005027575801357996533825782630360057558670923510328348895013335023158934967383007297478557740608358705255176983 +7266683495403368714481550525556638200455893453737443660389337826017810731088543589057015038655883579293647540385262180527785917 +9515154749550773405200793344557941722519749405852747512969927332977178984307036495472318127241347248808326472175619457986406723 +9518368191888409210468669956186116750302181387390597344128917877355951593149853095697850106850745402883068932235000062455347402 +6771238670568019918070196745624606843885430053371888544619960477580892136283363206743364325496202732003423537704812210825777485 +6482929731992099113054720411204927077100453390882438863808021020983016716953161187024589380636187873278841457216002930842647147 +9086580408882584727439595283961649388079282398490521121307077379799427026353954996467076132654318458754445407020982965846766574 +9211933498567658239813690464315557516873798138696846826984940696375072209871675004339676866312551085733908702422475770451671522 +8116601759554721517887607233127079564923984623323605008410478991117346586220447102577578587384958136197341044972808673740184098 +7135146663669772129679997894669791690347234150535985440597324857772792781041329129123706810764665585549577867626836104138395091 +6171338134734949666403237923355751356314985925381428193696582138260452767497882006262979715518886220518431969713213052164146287 +0631430030464172255146123719714749762611746753803707576735960990848350628824677285326419791772891874510486806367078208015683527 +9792995724212035781617488647953282247761468865566020082106220151676123008979512284726786406279413459522157229217252165905352918 +2536766618340462486482122665210247152075138704341907443423031644884751559830028648873890163595496896608683668686938870179657834 +4207985050934314372862959059606329918091261977012574425026916852113090691158385845006538026326042605485030114114277066770366995 +0403083121980863784638665369135497943380554934143397132755814677874063414921737247611566476833724738395380308234640207731607371 +3017035318366003181883008020161130977715784016586015636467061595058652062869875350107267130020471568933409734170464250203156306 +7978597058687788859694752086622825044743069638461579429486476003366615826282892684888720644513019714307671128574626623134255754 +3225178060856930853571262377435203804205020419212526191171244624283460959473575918723283729545170705498401123341048944419740415 +9391613362743685159686501403436221835235931626375947101387495019232220333271032937655938586143908770909065649064710101953494614 +6134742532053652776330925214492259910738874571044952830222662538392022245336085949987893286559101791675451325204933710093660895 +3150091954112952712726674404458997796890860249938694466648235513572474974162093062356467502005784802581269257972393197375619808 +5518818055326965608441276354480052128774778908643152331498791242682676747511171669510551698928872813137576082004507231113018234 +1869442353797750668263237704646436989107574039444714294394574053634166211680018322567901718394177095139221750525981664718345874 +6533895638718548550755018968962814261467954303937753456123595613702977896395623181616704065331577434094624992851719491928609615 +0190398690201868871739512766894228210227318255924906586297140986714101681237768762073978231156784378903908446734627746278198818 +7031625744207762150055633900029138852849133531239672839021846058540394772383588961074862090538883314417964060795273709151209027 +9458808924606129446923894166510777560369040702494184195952257501705875876785777050661631817125173124207320372163502777664965913 +3571992627283246607384090417534713518631913006344389649603958134572814682981286787015904497353080342805307719694940285614675633 +2468021847965102322233545270789228088801077254585303970545296705237224231512698609584385993582329483075661032700231251161660423 +0192713398720255736106913269405387578211504390721637672765578917354808072397586108294538148319234789867624965197532780016350565 +3634551422633123938462201786680417505451646469018713382722643171537409460426340292141021655461958038710203141816197429713424286 +2547844227738650654059981449340163279595536790994781981453043554676804995664853696521066106987131049223085671186846778658607485 +8528870512517578936416926762107480564174061090150629794502095506665701012457777925443164598343164919674568006002249856403680494 +2627259489744462745552373437029184782032929858142986221112288622061305496355657681071521590855144706970497835664724912122006599 +6545391156443312660152544214378226475642958908136790116192718647739147551525217967757101506789062053773824320749961986081365820 +9807243716856995753119727006541625638999515827040154011379764506202860312616961954158869599676568905867509226176218277805150630 +2073942766462908233406965969896132634247796801676354098426177499898295515362604488579021522583028021857262554533983809115858178 +8347131968232485092731301472083865394183193666260253997648555077371404915951205500140212998582177455804377214724298329527661383 +1594405101691517143629153539833183982495777310394687166385933138800061962945009756356701243200756906586574077923344666352145020 +0197245967822981167194710233250484229803417371108743341686105467298388178153158024123847938905011673366872874417478036778662336 +7258512385396982458981727808434299320938547592708453258132441101938548683407696744955847650770224320115020029123283233005466171 +5842172497719189784583681309409508840094316855599405125810349460717428156132068389884811129531014665690641559952450142631134435 +3120882941134196144363251034342986015603525881548802680508329909569735321266175483509995349121263055144465010373367684170785550 +4158635639323481056067212351252293942485312028596582540399759383658505450921291350114073361212206891492359937785370602986743900 +3248674367436665982259747983227106329784209726320992248877150489251570771053020539956408865849029873307934125408634817191607873 +5454247998356721456240658773994350865596498454154991883699218368604475173834985657275975908620474089305230670921518592231403386 +7351062162946891390363227270397968233004651810177775309863584286429947693132180002624305405957468154601633114090431896806560396 +8578733043161456354664012245929168398575757646163138337526746639222181428391936995137008559247216584486677942670927998985802859 +9200314360845834380351667184778736242928721806415925103242410837162869501355174790557366226463713207146043374288777068201363073 +1884840380691470035098733943006470724355475360919052895346183170077857886208887006329282319454011758272954888828936243319780379 +5906121108044999002595816055500975401842454091232971454535066145382445630442880279299409925299375855072691591936952415415855608 +7427547736672177870270669416923472904176994726042270600865769556468182342137641250127961474267696372215125069986572926580684930 +1283565631209283359581543095011894542058022145233771315616803743285668523226044837411757706026740916352039927306329889089649349 +4270078841384513763036784211367232205576108266657423596077336951308763566593324577257390215814671195914760219863020884678789417 +1094797252284852472558752948074017934025826602284347674097641546447667688812462875419412834529814608042996511263599028929742238 +1967645060681138182820884931078366878249535027007771792225806188604957386423876036386080705880427884926800392921951044407013636 +3181566611581199324907272529442720663293855144200609139564151757908337725071341353176155541142076746946246524270499526229012582 +6539374062743440091085243475167651698361455560983223707251591133463685518000037795208788191244350973724875956194617703899137414 +1430821502040369247348912270128694893351423782952149316135243013570919313886614565161358637079094402119428463930234964822013013 +2907845492061431274313604396416192334514224786448855288597321564420931438182778758210128537309848899766746069430202195659612336 +4975634130177816916191624410591783555638901610254745557743055701024321622685828565315892206810167787457147305784539222040470909 +3426187794037654633853814952285180680406547839298481434679476618025289610702493051123252356926166123963245694601391065308079166 +1560694708414872555648575097110162943981198563540849019446854403562264846931619983425916923587136273462584821828465235074422917 +9261979271695865979574032703343588466946379682055875729180379739323087400969273393174411780309814669530363099378105348978942035 +6060070323917526453062047842877465074776912567111669196792162790002591482979059554932579116312700674668070694919873464603223754 +1894071532823340571620020715489409265478540234593867913381245139145940908648942949383027523114581382713294341230059675979933347 +0475001838386962980608799805753326787172142674741106499743819708928855603482127536974433291922816520616537305575646417283646800 +1407873654137191433607314317494815145886982224527312502679184216570574189580738684704012553837521804108283635888649341886347674 +0746298331021251518403002440748995086111379224255590828431961727677994867561042572035686044830391992128329363200090117946273786 +3394767633292754355096521352490384715569381533683416319072637548165685703657478638050012390155682264692286629389917053497837436 +4659095138863302858042866246863612004771584058240848977405503610643181773657781348444480569875240316081167661626347187543347285 +7911879124009359945066105568437670726515538210712672838972140937144499494198784053146413298370949806905102557089313540518536047 +2387001299041180490187017325756798839893484841126540625119691321202951445764540357103908077323391792392357443606419429134662703 +1311387664343269938420538390428981548672980222893983525849983661962674897972783374663857036358464131799985348402057774789061484 +8274461005931054626156020913275021215007841002783185917701579638808358640962009486666957046897388731341995785176631521816719220 +0104967747699292212229371707417874791998945791320492326714053322534677090261456035102963665329391198512545240166335302214953529 +6438295329258114463806507486440728873144766916788136148990852634546220929994618127547833759089567150425084541257262887419748944 +0763510822819190844430827630471119922358130707131670435790654181644862502449964467386506947657809851105256327118765427086936245 +2850131634530202363427136221800593088389014446941597649063703366121196898996686256705732620416208851855006823708510518951595042 +8751603030878422139220116631069675693727662503215348707642267571256820475012856064445690912370994140681017385311381651837184429 +8337808674514385942347760110022916345088925240957432454452424792146056100857933925448423669501901023480223134384961261737051238 +4413770733662528673309544847059059161282542697670213119975814848684886926540572546980353008826625285683672206383376561174283585 +2470179376522057019705141639334564346002921054122405868536589549939044185327352857788406922050956188186959812914331458433168181 +2179183768538040087965101925166314083741211822328135691819236749579736222030235331381929418960851621953749706426251383153679388 +6697638737127992949029219495456980219928837272379992149835670720157263752765848341694925460132961677719444757263352390287952092 +4093217437367696270862739890376907333913414784572650839225342564140670817115158483271922969611210426005659701735005263854191680 +0504868284114293941201391805305848644129473248052919285116546768968370400211720819698688057290313111673099410825833594935299522 +2193387285480585675810105037896679125123370546559641586098343943853768828725430307316784909701983568646060993578149869405653861 +2934591075689506327550526527978208181872636707486069235044785075612292358262482648814374144402086535106182407978781842722397427 +3764043018357659628728101790307107498150768318990432916850503907920600389618490690749145152636599605594698036095369786495988258 +6957155927715716313765479257750780421880426026172174055737331392383266819616843853222801559499877374057106670897236097150948636 +5427827682310804721340200045877249335498355065518235785095376215323629999016684789788057540796773407165234965827360566567682463 +0931247633662980846735922260231265500967979148521116776842985570907612389971711829938070907548905323760203422391349321697221760 +7882789193682587165711403864901445874796286061561018238012019307540878080647271091752229028944021330264150343818926048069144244 +3829234846305757977743751433953176195429230057570537421684266250781148509555390682508579644164157541082064669789683815095364787 +7604939164496265670478495045765555257915418238903189606069500034604107925625520169975834751123093859423683572718773062691097465 +9536565688839857043404968751102502068918401627692404156712144798677665827278179931457967293092502383995754188611043728483106278 +9597088053951878542145390264364113151329590157432302502341917977233379257169202042072712802096599668573120272798291944153363375 +7607153187541747861933137718609189822023639083176590969309128534131402001720049300131597786926920331444518080594310667529269598 +1927075329456693896562301483770530596686903351047943440879405951056391325510081534440975417269136288862213745763314617080452571 +4793088300460286258221203503876214260288694619740795021100096527276740956582412254816142704747725930689538001063488725674114094 +9964651944235263688993656306565044507502459642744800444126035956827838480470927964887269362894767336619648517065502062761621016 +6129875810846273188094595526245494198400294797450838878997940481886389039479255372079045644823922532826285987109316717080651452 +1158870497644577891842220310991400298247186591687353490986090963675370236688750155704648708257105452733670085922368068618952508 +3951586474153083065442516047977847063483146004542723388081748533379792081337808607910165722930346295608653437371334690508592931 +1348798137260798951844725481726752185101778730990513125012632961668430932041485231492712161518123487434868038690945449294611190 +6621726374054578571840509185550584952608718450715584685612358931363303426238432883301985179247942967408291991891183249754595852 +4638603540391846589823234253913403535906012903538328082937776681713183362324668976651509798060188640593955628086698902190682737 +1314136004568121579468174164410670114683609747309631647110337083615027244209788221757184880343776037559026360829925134683110860 +2743916110589248861852284646250771021137678508599837301158835778690463663553082493519868901155377573114305779992715972859595723 +2324106156194950900453760356879741607730365869344607490682695136931314492042609961479060353962654666877592314030242319785217157 +7037168535189901999950898708785124945709252827963059451445136357835564541820565609455192709322939082485154440865255543678569770 +9896528396971770400160528156211153078421941311469208430939294234515837583618887123400678449175339595403746664662044325400329517 +8261684885541466594581605303831216257932122772017718190045767780517645346911853652853978624682959833140344555022435113362676062 +7591075563360347900183625940068873872381827943795851765368024187038497412028598183964746465300474453614206176090307713158346489 +3374371123832409332000144159517441149498304308931146086397279524284139118811410468196667079250510555507523645907234786379143531 +0501327353064942573752656984727083020738199901757943687656464477159443799349546909245912076525128236088399431523118269843367482 +8613396151458427790408589367894556692924575676708650976493092298687011565830641071449135943255849354462437020422889320020232241 +9743887743932345248629409670762729564753828800124861494449000192569258881214737190612239921652995520457700328791744959299446113 +7181302058928852380763877100101757115044093853940723929209952955676211472249441292704903663335543383186649963257659728968301134 +0388255348661563721653151594456333702528830265445870529546222473972548233646402200780627751622560467191291529956825231887562559 +3526669851709736895997935055319638614407522325558293993145073133898821442020970359820377176459127571138998803488194418913023474 +8960318055715147737928173496291103297340011637615159200331936657806625012838282253984371770704650788787874077528191805071976481 +7124181681951361717176138891836878456339953961111533220751215226320942283255447821785246102801554680118941275936544271258524058 +6005709566240667298294089625210827175355499791073268532752944587487068732200218887976443970173147332812756704491687536620579417 +8523733324824779870890616722944885692254782796148238417811553337420218355790067774624307813310722411932853894530043351212104621 +4208314480849782328920489549263981924145677154566910149000272410262683233827903445768177081248117495913152923052663893776265183 +9942787773857699223811513925994334863462094677096830079955240744467436994912827703793978101340756041298804409547081408298801222 +9436460738640015479585458718857778968516432654013043215917075749663128588920464616671554576564466134592276012302780087016944245 +5682263919237157166619832653999221365676490237394196490562353201595416072759662040168607378605240879904649331264075658653944374 +3188760460625654041085468641256306887215807672289682883500357801180231951265973876967365453175824452139544361480252271534860563 +3489612971636788170285039991175495323686795856259626029428439316305263410995491370010061781397716962628385590075791887118786041 +5311327330508694662530309261695611418858308192623059112240071130884198746415297821301302430358551661316850076909001762997643991 +7081644935973288072050639441580584025029531691351232277986672643518925684337072256344249262810387549624475090299911463668556533 +2269997305729536278296569991307939885225827949212711278683260974334694618965859001446604029046584493463719653785353337281283459 +5669018591396191702482439366148219818778884802332484502882028894694251543216632761518648364549018220678109701293026473478656784 +5216611642715035439533559692613121139357309471021194983124318134640525984115565217254793009252347891534891058775512107043759248 +2075136627102497321632846769129196363043810766007725332614332304423370092169449259610020916402169233919813196199842388493441962 +5246508710864902175515674209765293421695856922741891032048768583212886538286815573096956696932708558353574597425799726517739782 +8036312671737074585001040104552777443725646270711497123342871845885341214930320018750607319709014145767035316932229927380726251 +0928548556204438876091471205246192546298228533150898999146677973522875037893164503792472130409496157160476844265385841682474739 +9233703771903675834839283547585483443926383504808637869079921199402511859863654798938168359018632590649252624952570683979540091 +1817221547279336881324191918747916105439761395909101897519754743532665651720607836479601826466707285601138161807075561528644357 +6507813758533570073431536118758351858756886547831285242021547001802317103965340085013359438004591048652288218222633904693766526 +5939230267672950104631374960137514687520839197558900123956846637455545802654473302741665702939840951606373658402834814254928601 +8899091999774572904211301414279903590789989799671058329810205159495566404297154849221520843334449344645035156970528552300030222 +6795649886498390391442973828383660673784035290138239807079635557460618362207456840361649348840778636144683493328779008559160150 +2312967328418841937893181097506584921132253955130661944022425054340098558673444397656011485902801524597729549862183491017801616 +8364392130149044788968141245875416756772094094121947877022476352773253705820364822300001844264751712000950605882778006252697459 +3173455702957684349309232875767407323242741753471239292204928826580888927330167241766284958168551480352850879501139241966892452 +3677753858072439789438100033278254013052115660199724677408997086225534466921024361672151595117485386004966423349406051295735713 +1033555069901179500855772626876468463729168610634688507726870943003645664213844575844814874933857419440136881531279514876328051 +6902431911053955232595350941009434993084829394137562877137241909400659809326832785642273291709998822359902244140526746390924526 +3598394230231172238038637497155035177663453584383356974229908339909881383831792443286626244348781344679035766390554002582686748 +1986522033280153369322100953745103819643767567342601672718065640720320991063977205827436045236651592119137181084201370425284749 +6005671239600468501156161076186950720461257574394357624099400895752471978287422522141370855745760936595412914180235254380961690 +1248432656688783102677099988123915947205672369413239452092262942922895352363751338938332396260328925306803441684111264080959934 +7043361968005295354938534781857472787124249512274641194624952254981027355960086055922235901916884500390183949326239914435749876 +5900769760705257904732141569222453308536443186947518812790214535222294555869866712002026766787105689583023017833746154510155688 +5602689240796761919744677404697558271913792067417789140292202440216539736230658588620970781000844620550456371994946471279052637 +6983815257060123566373254198705908764725007305820906210985164414734561111049679088421536404461082246253221142258100082860407483 +9787291977224975917546280204714841440857941451156318425600052380180319358748436390813045531423344721155836168161085909646309385 +4242317405737007113338681399774018133792533519976132559943781717607228562711266632582303687475696334211416518106220430942562440 +8001103370755032373531777436355301854609759653124445242732075706459884158212645376386153155459030396899392086527430085304137547 +7061352207839212087427452963168038388774060848015551117972221521512460122751953993512403134647980990954916284061921026824432536 +2639034110356917513777636397443311536328136804898636889890450733741495575783711662670202058347655591164148410498512181752116112 +8649347704311634713230582416874077536749261618798919119607870454970552954421518115988625118329175121493449500857931770229652333 +7998158841528743978260913418602919615786831528127648656825638874370061930628233997562266898051264651939263641927874506380487439 +1169319905978231587648398211672101766334393172127691275996797499245406750863796639040056909542250284633880406494091612313210866 +0317572128063301907416088057966656497607301508200217804677889135103287897506118599601843251561058377858126583470533794642274814 +4690147674695890988264540020570125563698789420115878750835681272646540842126992805764557197985389253255527910197264650144295636 +5798557941192958990661384529989091262630978788566879170732007097819131006336590152363030155153413704404260928053167322809868601 +6850987001199817141644762090308087270676753863496477045191783385382914059759989907264265980509298271071343239640773196627310409 +4389027546704283892663222266104915372633460042956234973983464977451929104395584092104280081159181521725125037452942372440981206 +6675905477554320026243055974304148345728455542853879050278442548087322107774875373656985055242105650999387599625064478196226162 +5421451962648003632206646262643905337837657920982111146977004270602922886008501562566012949545116457517171981566495094457953170 +2653749527617823956383177123455781619052217065408026747322738616321944929384445431993242259268884094539560852964142946672208945 +8593461023191475746268918563613277494098559522521079968069246435865433845367704112809898386514701303501214965468624316127309978 +1864190494247307693037266993358200097794635502447995520938928657994547718890764619887745240985736437934807473704761456024575292 +4959121097171972205063831718099245216438006228886790229146916547085014590026042332430672777678300137100806650898573540666242384 +3655898393459642632004541036291612193425851747783279795401104056847304510228417238922574433467131806129145867390315264166063875 +3665579208506648402815517926487551967245716952452342336509872884242496327353828450388024010049265610889985970803926589219237453 +1676400095916770050334175293677803863116549153471866784271105726012628596876429530293900006500470621643237024986843048814960581 +6303887230138382981362522022235864197895867379102358980372057655831897690287262572451017096062107356032913949946624176978299116 +5005151176823509060156202781958197462286719252108370751542425598689093613819219880986961622340717811406826396835254749852492414 +9810879199072089316354376552239141698257935918222963398002559226752773027118660428719265494441568442234264661966547139854597318 +7873099218250455002270603753696336287379126088262705830929170584620542532170428705348694262962278699570554235741942919271113853 +0853097378201921331418681617335834926523454159799372408812168044016229123296540289846574743942294543985242874224122532498334672 +1490594866688679908867416614545427520723745911612132408696699789261886952468259502210832466240663473598353736119026236378375966 +7870513434223037902717553793135590258545562641705185546860782749440609431080399391577848963291799745623426666134231053217546405 +9446201870461219368892707181895875357863356844636097852807754420432074309846001997057609966109454410826649254380546761753175861 +2761057480832813584246237846523502525922933474798627724801784019360724008460215340269094831023982649184534291701279975122991786 +6747259709148196213568727070749193520741364779274424639307959421349714799461578583982498969579857164144199424813392167578660320 +2903178417498559526330742642780679790900780361831699904848420604973586382034364851787093357442413598696765925470890327590522104 +1850819839601928427102795504066478105300817488452603819717168073205087377109510653761519361878295067555599575161123548496116519 +4527800014440544203257680309603709183834604758224679743597378438522340853371773245388147497516647805604078269186299047096706230 +6498133304861002880698895980139890714606455707071690798907848409609045542981039802321508092873754941535671659985239738644320953 +5988405032666743320254142929561597654494413403431947291535930103688912359712380841156852022207805099821357330225186728532082090 +5547768444839618341918220237864475011631317876613320699268333350020360283617553420136149953194413589978795210753934389762807735 +1597329107893662665482951708145282526843200385584016605115945831916977370397498490572333755725067314085934032787782477551454989 +8425608912417288932359069001529317862434139520899814966998471303722622773872512411876898983545914085663888123444680470167948505 +0434379133165396213883414761877309648582182115178764180968488658223225128338783723464562383576437566308911246233632408447704048 +9663996367177301008227471941256368607999821009483324708241288407336982656214907935931597532047696000543001055538357367423684843 +1080054249718909513852149584485289714580193379500564799748250805755830508642143351393106365614646908548224506838553676335276275 +3599779694595660288226660287150289204263669082115248096828431008225170485272045909181946378454893228880239576553739907190878426 +5885850036699891408378122906193395367309415591691241926759692588330412581784093438940668271882079466692153184239937668982303474 +7878175454828248795271539884473981581982185106585811627218428403581816331240364769920118073711196193240357920319448764087086718 +9383157433735677366088538314691739001878394272868474296524756761536638899101995873845026009993980779305151574599223381548532836 +3668859832227797331952981131294963911962283005597571432352599223356213488863688452877543275627906341710804574808946808990599277 +4561238331796792049632393877616772107198692076748218412457533127281687506143050581347703755397549089646358464889763941628738646 +4287040673613995225940479030356811166110299081667864088662964809883579567473005653984288637714354335572597312336813053771357145 +7067167172790181728147512916597493867072803164173683458648511730308061884633961295638170464699169569357249648229427546009552241 +2411562579887913980455816507682929933659712945876292627871263596090842188654178242939374681016381916432972311121058175582055725 +4171582020877122298082911042822107332503076877376941726903575210668249614607891518640837866322224789915987615341840992350582413 +4400620040237405249285698955673350511161843009038833792839643496604322064832266848854377214512824709405806535993728236489108631 +0795420398459113177519012283723869747981824916722015034363766243369266801152099213940570568038081676315926730528271844090048984 +8736235449371678977947912073661875499227319547901313273871106645387069884378420956151320072268281471236296252518071417869812398 +7190598905022008877003332225740349664805580192380729307470319849086922459614014566654582713365646665851557294353631593510036953 +6381552204220558217529826167930690334372579521383903206328505035888174852614555542283456959315756641772011982607288085297853686 +1349874783384878827773365548625629981523061054390679273761674753812182403462337719746346989770142792526923785272095298042279054 +8890444037528702976987014556298735324481383261103576223990679497009263015064472090831542516594399577027449370238294863246623636 +0336415324118608522678012840335823229090847361254516690870558705257294993006716462941193893800422308988102399798523789694443112 +1923359529945753787122624465725573807149022119989195390997561541611804328914617110055244794800371476974622186846094996027068986 +6556505900937451676724626863029277508817962303859724405355623034880805936586542708527460464895044273023827975806326940478307069 +8592969583414428893231495897818982129258755657382413367672348099201190652878055544751096757376276397486791497622706985221853529 +2908214067134856191896613437551307015999440525676444599333628882928075642102806483213033250837448355509306721853006604031173677 +5140214739575524129341946985893116412008106149871779041808900801409064849278864166852383463573647719274024968463791254976359299 +8847571119606652336355242986018768524604507411735194803338041421026584303361841828532589747052208612636774062068296366532983021 +8655354613872561646045351810915955927958378666435016019460717328781560793954138282423423334855186987151419088975164710160358441 +0049260799204162100696414303845623303274803491195157588921793903647526766706979672202295037308810197036167390246126788340972486 +6425819895447745350399433314412535917516434906996482513931003678441923085763619185652238830813722611071936854791527455947241273 +1522067822697883751451720158852174772439898925807262503934468921130385503850561421474472046621939053613032754307326343494446468 +0373529669975958588899685583408106767873002561071952257411549791159364132364256139331134266337194556486549929770686277721381623 +8699593258141567800940600698126100601323395696274203924322395263563919165964829715731584976772134953809087705318959395613991572 +4912970610760196588068419702343479920035578132234944550622679160245214714573304402887577273891607583288099465093463035431694610 +3905758432785910691737893861264914959634002377051354781210095863851654985098474884630562136246052330525659496807184083910344772 +6004796506916845030772132724200525430268203485146341239363234592583508467825890468047631822102162429469995405739029446103026786 +2936254882880449606977442748506803270167246097479977977827447584435796513203817330397221813580364844509174311882193765156654656 +6587840516915974691152625347076034414185867253449830309514472874859872212804040804281552578684775759016088925401886242253588410 +2136162082678649695650433358835719979490577896244051036608023014229544782516370023231616157535453095718156293168346470255374077 +7531511647981041419067438947595659704304601529630424537788677553031920743289991068705064547597248153946867352942473723492529159 +0056165493810107660174271077633956520600765977621759777545425851195592595759244720831020706108951012298629188421930844092200174 +6642191140454110070088900972029229475358599582028206307088525106506003848087788102905904900131739063517620939825620312143365317 +0312737363283798477941966380713988316123472457978241774897278441026567305606285826664686434652740990763564567342068867452842418 +0562084816143782025599524978888894444210155619376771675521151554796594041637061218094181094152665322539245369886205219847245940 +0029405789079825038952711651290857359659229927344229997950428696846444420150303484487905236128836752670607196713086924265912777 +9562937968844743614454574043893713056224816312429825464738549426759328345347674841205841958347360767579803402491577142236137115 +0533906692397009484027899629862953135204910641799594877569760336943034406380230261140675352675702660262675371335224685084951739 +1782949915965578201764231201307179274400901440982114344774078855036237671988151622145580473895458193522528339933303018706591388 +2624100076711766326191346479084275280944607050965314374143191066314104835834695035041295363869510686680641979042639548470149337 +7092183008459177550878010123635992307868170928762105253223870251579666192277376903965022436031647547645865700232025464698571152 +3958091554628717576397311569044621708700856680671753736603162545139430701751294444844592994780440200375396353670083705904818899 +1158314027781793782715830446196630725174972102720930236759032571130842037840584037988906905557214975416804249578167992887315936 +9206031432141826113994005343808438088835006886050009647865275969654889753018433197685269199002634601456210696285807629459472336 +4978328489062061200804108082671938204870858983057488740461332416429168822452529945581949038775475758752323372301506063733079805 +7738055653647518591404108351211994677622659177154884456890162864455435902069510745121823508583748140331565330826498449431014143 +0674653178829590152076432119041481195951363053106794226287750227106636545367810520707295978843237959311321898828560534345440055 +8038620990007430479290370430002421271720574168054663731040351369363896772189503927548376910184417619479114290519008175905383545 +7118330213438952390733597389988833345665275766596109498963112726494459652684601428898616858980054673653547857404417055361966561 +1587037091185968630608431240360245295772438686943763646925036642543820946055469856983284464996007864493360253900748905383563580 +8745104358873817076497065734218673552778861792932465886098953358042048623712145278501699290745480384052380874247143800362538370 +6251997645750683187872814541336341689788360883547457593458471177846675162375769376759272289262293759496680413378317765946726569 +9307054144320271279835934039640502070988256340708987512314880763049398513262419383032058404111149286657762258132166819869740391 +6208679111601546062938461491996426928719837110414091479969117767614499297075986816870114797028327349760763930021316392342344670 +6195338739848107103644806413130142942032473433624064988762100818410644021199176248704887358790343589713845228788844183335779186 +1206612776301905227273380391656909985775786692526546380296204581461396277505352115889617471623174433491331255641939009226704644 +2188221413330256108594133828534885890151834345250541767462459786862391647167810214086311680548340646481922395107065550293841987 +0422053725280997492804556037165046616311376881740818579026242231815216233417949738101116086586716392810175144442461269235250030 +1446402688506317648015492449267953446562377233978895904366659114613140191253374875252211719488638375761957759777329556920444079 +4742234209552120961058674900214350131325585573620862943651827639544267432934381233345762116672659741592229252195548093031985971 +2686838533752676886446680927399440863720061868372851834815109578907201299828776117754439315722227995606329153759209194448678185 +3375830903591078788002755338696975595441586005976260188707036346541857769978992173498220429575524475532930877665817876092460756 +9351987353933462923395374235495972847763917223789357531623204700947268441087397398182190787387926517802157283116535834305980686 +9492332135897364643664989358260446062172464533880475533980243165095186005367740562491795933632109829107385087893207785894397713 +6433768862702582156901779207532027093153001807004150434114821705300991777142257598550961217676112326241485864463713890885328141 +4370423630135664116118217486548726531069411489498186055038679029605744527511210064431632487827249384739496221986798075711280160 +4424010886610887880437216748921409021429656729228727372446155715321241952848523185619209556774687672654909818027324033748491821 +5647085557243826965464774431484515106691575517669395491646712200606275072075247550415886971532904779039992053369051837694948246 +4648536696394853995288135284563546131345084094639405448329957974626886591260968153163248670156108903690012016896947562864068947 +9335817025706673645248672808655206936988229988506868855512246059364735571373658692183981236508753893624162179393300884572562525 +8454550045708811850843967602772364889207411447386933113146884803720821905371224276398839301167373376393299604166530531671536817 +9469613971479940226446065344175803391558840869442680476409858979317446638566865770776832153252927434674599753345041690804354354 +6652209314699751202444785726308628207785053079762876873537555813669271605409315256570040001991644970781331622466129179499281915 +1961444894849498696928429106294385830964606511500699941607840002024216695044414216800293243595307021832292431154543081449089313 +1962091622249577961316459709116638715944104552499484128514903058588880092425282180366103208523455810521574844650965568635887113 +4368421159409460609897114428104781398719260897374658539676968461604386468189383954460163078825080336107872167576349496495408685 +9649536539795842583228826048018046721092374785553431643665884938389496673231436870435149786802809160883735413992798925929876704 +5235671264707293479931013726706891341489539237837731840177933783526465474894432047982015526419973235553573918270319559547403086 +4039735888376366609018757028506161014783678583356813250677778613982578318008203758267120384291005999279443777316382864592311739 +1217739163779382661260606469702245656794710548611797988738048091846590739807732824364552075368990683393959928284317293320069867 +4916615035875154768600732328677330949063202236875210951540534956404960395079564144372822026855682749788976197007630147709880516 +6877745432713411450666647170018266444463633969867263093231179811756276204043142296040101066040921365338143870829243201412736978 +5181332217031028793650384590762724542630876907198977592730043310368021901782721530053394250577461993296684728562642042745578204 +8059743569954838104598948856222744875810352375978155248926213114786009876969666524615446586588275065587752809296977777941299366 +4960623850950107986976011274245247425368704858396317272530283837294761704449561833295704741998045363670811422098828233894939307 +3391941685834457637274247913999521308752675840541536848418544996656831007389255133631777457133457716285009673478699826403200309 +4519130151833287959851246691876615538787874185186936205145712978930546123861640155395430711723899715676633532061887829247109590 +1676546877970973940392277995447472865522499015178996256227821980732651914936762181112328377073844485649563827098289006561421904 +4935465846415256812459944886042442740776629289289624483798607070666905637221701286036356829140962976615020452048228461943046845 +5324252244416512745242538847575585469682159371913734134263569496276780441518422322618507872746192259540742513974372774167973337 +4061947105362693262133270759088934938816594942342421443896871674922874493598103930292152723174036554995062078161445343075609331 +2764956618470182052214734830103729160245043907968784028181813618886443201986039333396356809943270121754709713915646568740151206 +3370863211220134665256513439944166456784396566762502427753466744044215577692010267595936563128872570057590311781066772952724628 +1434093271982951335221748052776429614044261679575670437872151960576974124250079774604982905427771724749469647670243244251475834 +1955236107353798168181042526280355939682930522521300010742221556820749544725933966041624888393993154502962490618839145486969576 +6290416028546495661266540757687391083370341491184427521635724821327522535027514382783824919995740288516481937621675853899158115 +1271233716574270260984632585239413670925887252984501086126979534236923438493445631129740540692858583194337904806381657298903618 +3300535021488010384891010505876121956740827701335944021678777354223576529240510607458801670448052514469687591418109583216371899 +3485417441989301906542262637698405742722722240100485896839477201597733807155335120248775000837222824525999750714790729423644465 +3411348746879651113262757268624543364109755847832677598538228869268434533167862688737780726486206863057323257234642767012961381 +2165814758833781311422742619981746792464126525574786109619341095871177233773859607236931880493129178559812250332066463408514532 +7726034975123054527871123751165334996022991283473243555448093241244802428165706217812562663617393968824053366244173854470750856 +6095264928940472527983481330401867205683258753243162756860090551147348417813873221392674935402798968661528287131244490650915944 +4846998428123345187768488062434480479219313046695253889842680691405315693254169660178067961065703759896641710892485894972873800 +1685615608144031413476448920807056587474093195656920132969415782126007882727463216577625078022336565533036012495150175153154793 +3655761747568001155088833871531145294046756206587264284578974630056937095900549928662096625015556765632408742933996850251895951 +0111954704076325038375601656210245491836721039674987643056715722213989286056097567862156918863302428121309646750177612394901330 +8415584072851574023474900634488379027144152280913594573298990276864512420566377677406310761330579752357658832083046689763512425 +5360991991088493887837058948075647761797269956784924198250160336333432026534756149206974204145691725795668303825556473066440146 +6308534942697410882776557732300657800112150355078878291669302663342797528117730903433654703116183867380240849386640798032108726 +1472159005438778436757327262797064431019220263757862409221343714482887707767271424863105342534371712556647665588514796238299461 +7911386503568215258552458522450938485043355503569972624134133652411157539448177529209200030556438822689089819514432813580599189 +2365679543813190286281972504483805742897733392239676734466218958339246807222637911353738424980809257547217933600526785327528737 +8206666715147505843269892459203855284050942439891047557154055608815102510800979065603928242478158845237310517156379667384978425 +8564365639950704666389094627317673103441553979461958229265682055180735168285879029143428791049454750378104825426147990982959332 +4829102499976584107337249497306375535937865662615510579219221293219668812136853030161705938471830225621587949094705819774556069 +5123783679768692703321434015951275136482214434898378095577232958805557078721200098045359198446613744587543829867662788092598136 +6929950567989350660377223378484336709625123004431484409297585830087971734242391343301362221372782329683381841402343240854937627 +3027430314645534840941417243615965839776590781088050407903488696621208213787375420065466391992905505392720776395628578270203968 +5561557545896908531566696290176040220677116087315281719465429326728445015278079287297460019260738872427151255654689514071849986 +1828538379287013993210377215462458395873947138158932974395044262987261065763556796656318930386507150893214229366028840942649836 +2063121041445493166725834460800448260568700963104181570946402318291105228818528098805135391601388005156357001178185451101272800 +3370800872347836800855502143109732296211615709327616533336180819117537969874343031967516246129400847851680499950308763513532698 +2261251707986576560067694894090522658489287994163408241552992222074332750687259975120713193581210288235466997453538082950570613 +5547333609592722700682015971929817349283991994515812388316385424977175852402318951978198762120074865379726347089860459367729850 +0402544338102912384265553136946539662518389179790301064640333604344279296879018653656722109731114774692564467347146643170192535 +8993673525857276993127971955065805434826324155448624528360859774191561165506877746667502530039089420897916681025323026574636061 +3901599392791219781553830625484740571991825540511807354859441733493239937466053566374517381465227962291796387571020809725382544 +7574422026948740113868646418886560822423385797395522913481336942061412395775696211559412859927044430059903817069457531153931671 +3666927872751409403909634814246127285265889024700225210198280286825210509773750861772507551910169158603017385059974490577083038 +6432266277247351230987806521470687675699096976848067098507903096237956371518053276015484587153777797643144785970196659271124201 +8805175031746648720776866745620376390338985825396413633102505797368825729966186841638401277578013322523402620704692665720430906 +8623786671348700175861755678175427019767971457395145977447813890266721740858466375677681653108760786751356531141090929913781526 +3195648380237469630693855838585465684458789117617495694729011729035913248141836338293691788762707190185659273515849140203833275 +0597402941284234251455344083137079916499435796408338558567042346638713911874856299057564768580158647502818908352952487514100356 +0970868650564875484386825809898953941148685658470222784359891344183429634142985332602653810091182805015332286021467238333362385 +4744217325804259157671625823323830396696824326946849733799160663706608011805733632306227156863544912264884279245494044730337851 +0084125575216079610672037870038502647338343776533232876650551512543864312081271558094734273891229896844012952524148474639077490 +3953838752044271045872553760612072547541462596253868540540395181295139378941954659771594736894686221229277026890071243182491089 +1570455075037097901528554345355410026197839647129067342374536771901052967778273115724862482455356615889491970844930623438114267 +1113425143150646440502818701009316634243943336310155606852136330886016200376990719743484314150050963993583980070237708157561869 +2850497678307680310432965409960545878246397108725852340253836659713228648432015357639072103145170515437738478376685162370275788 +3326664187179079200022042294919767104163644624649594061308737509813605056856101998334843842071022724849290393928923994480353310 +5498899160503467457596940573659595416916940383633806231810327042150722695852999362605105557507669885448657908253818192962915295 +7340742801283937313388827545354690848045358633204829321755059875964474925792234042792682433581007092726558257928146358188938017 +3788737611929951605093399738060446796551970972956586328702617346062935531918635358396384018139217900704517134300111877098173771 +6556088236400468643295262663772005261144129747959817416429151644418457406109514416151746327186627985567345268051259611187659664 +9265455725916581745797982714801820234080260053210177213938914050671185571980281286595729366143889579286214314682943150406601728 +0316466611292764222191535471075816173597528297677167091929792260801201488249926367457821134968651224754973624583045814966898991 +7240870813182642979734405654373311183972717838426383954790596720674570866884378092938010438980361304392635321123093916926377699 +1098769261784660364965755629864526449156402293650490776352458745073647855811975287018649518441512140869999035534937655163547223 +7318570673563180482204840468935262794048795191854962451038993504158655689641517360974735911839513301305317748540458018312631163 +0444738341332226086089060414609016104084271113844087095538006211914540457624942221411569773470678982159365460154412977671269730 +7214452687225977763650799051005862709711542484791447215218763058978813997928801482608042879518784727437915756166238817248827266 +6180378877242192988083619217841832407955378736448569454941330027911152317402955093457538120111385592095583433817050629741079340 +5629107620334905196700840557766414405147144002648539402508824797133924214385476958587576623195377053042403089302692454844253984 +3318554062977863170044352262420619308431178765567333623647941692615442475752129139395428889369180754179899164103774056228855177 +1724684496632837895268448877487345538070342201067523252247951553639414362390924781555578381060846820725528271757795755177666280 +3668536899528123873671104403554809934477255454000919778770432207141030247047304569687086172422809626838419879927821205317757198 +4281376208790055629568334266876725167967493231201019877577819781783383068774238210201096426961674885308966481435486534817275888 +7577712459257967273487604669410934219901389126865013214764147583539515778668315529487762810385482472159142915583111160208903283 +2092476036780853093515438486821087167147843243339127524062892009959789660882873649427006559335981769826686296878365166850602867 +3343172251567689761041900651298644580155321777412234282871319882978374041565615637937824367712999898962026386244164131814440583 +2523107617535787966332348997625949033914601433430397210697903069041012073594953603672415272693571514416319482962732360550203007 +2354311337820780136418582226046355285087018441007811818178174630131348892691010862349686382003869512529793089372921566890105315 +1170999065500729991421364651545352790769082161371433138736113416719659099975772914182032395782369105406892855739430280792570755 +1474708015575641879072739292610329996968899496702225715923818519908679748009454327877467225863186123383312506444593821059541320 +2519230317068471671990663024614062738285094805102560284217361596105049865919692357067522503590280759438003713619649669926160819 +8477593117311192072667283113407927363527106554972169624278711433088640030184820529565097341512033077210672824164404101920707697 +5928625658638382607550288328629457639763193426953494665267869864411489469568811023649446369996193687172148618677078694284120641 +1646459774821145583534736023501469753322215654392655710781868598002276354678420510784519315703507997622744075405271188494799548 +5604976454282399594837769165955324714117059445288436250408738807427315530171196984735484375626981702836635871509821590209332491 +1535805646302302016352504827863133089568000814854822798689081283591430708834238105002833054643250911507660006657799194089124227 +5554971708100609197716368094861383459498089313293558900360605415466184945588159656825590482066432370186681671169454435798038380 +3849073431364569441656271420494005561747519609440641325324675204133825604638188550112139476449622000662627992498117594394096623 +4191048491581987672722516757004987334900086209495744999112552193520096404679809481469054957100693375905822779214877756331765654 +3967826927260531495518865955487463010124395050606347758705435971911583515293152693887305102817702226005465633434328368776552721 +0431488077940753541035965504802229919911746684489825460131664577839381215244988219702491643455403072485117531612606199932108314 +9410075360076281631174487118204030545727872004911310355915647215932318325233997786962419782803117783722823043531791703707719546 +9979899286512850189031098302495325193753351855488331350658951722090011555997930787556662797780665452620638874492236108755984250 +0652520709290586245394513350104966676213533639227604453023649959625928591554435452582297988094318689720723128469217871904823318 +5154138924950867908591044177967760020371009497593629645137446770819235001454567589699623753759581616298742234368099793781827967 +3473470305718567484348272233839660441345062709145235638650446308235096448960992127705010353860654270452060145032963280540290644 +0926811026419214969150491345419181845874142492157134486078145329290631845342202476471135442659122079092431512088292118497802805 +9354612782399796245605885928858715130206027789349453872862552261402417530015360315458931199778666063128483794096976323329377872 +9050549362931470711847062862659110650055274307045585443522892077774880073956334195774718662309192081402741882514069042809280212 +3388599022582995822369209018970901943500870352576118339149315315976479926966165136544818295802570738444044829469770910136539354 +6564372944547448237407512632251508379320872549342100765184348768929229061655816110427490635344170087149155652249787176557737086 +9304726910793034348084164624446844756267779275838929154374342302538079381563499255177774157465080422281305646532991405754916981 +5079495135477953063596578815549589683659295982170939553637877137130071575921564130005951724009912627770427858856970687414583660 +6190459320672966822341653503087470382689354052580606552329874275327388332913948910878502521194902477705810238260537165258752153 +1741863988657791139389205030367955495311579034712194218460880036525975053623461402320730996642095747448443229185546281279794016 +4885628580837209961221029576497258511104739050257941549091025711288015307691459924906488083252587010968968824529177652581775801 +0763397108119317156892696282929270919177456927168774057189218655020688126030914415663313590555442617356391166888501048989509961 +5152773773826793584772678203560374056030587370542989568481592013810907285435722199937156448541686535030998055918516459522290076 +1305239048493423602262076973364789510528152271858803400583562336066393859447415629511735131940444974630579121650398527184965704 +6991327733592729480669828930382613428226140528036104065114673331048967012900401318314500711300298883332334369070527930012816139 +3452427216705740390119289172699570798671134046758624232397496740015824965414235594660630239994200610360038450127723765597890208 +5175730522105055786800808015853823181610779386663930878893825884556056314059074930365519187914921351319328315180621551748091601 +5714910102916106131414571159732353736678835156815504392107562506650484480968671954066774178137781734638792008562430877641109527 +5369362810620715036409904025968522864824364325482506902964133711312038294053559282742499233287959154156854383883148516152426070 +0230923585180985083743915234845529953134912525464096240500757424789301872897111680226897303301395426146920411464973830228585960 +2828878183082532630135919486366960583430058092282156338048743819662184574743797715878229822611358535700802659064501248149009579 +8877808967697355352896283886616902601338873980280498803907441157295597934158728665206373488691687917104704283343745205639622462 +3315755549989540644308207351384163575736340700335343877848915517455194885659684732058284726257857232622941934453881121004726782 +3002928241602670019659934407449689182082678896433504272332382719662522606641231869982252041696766531509399818750721950486163498 +7932472000788041651204426873094409270765907861252426634561030946897515784875744395710037381070458145660220907030271894709835605 +0606375828713960156150750275120802286593052131450659403286977242629895839703700510410871815684746087294738637398861554742127265 +0927164114710800842124744207418934599056137681039495980981311410777428870205405129624149605463522859752503128770836084491952252 +2931015631747347657009129018143774038849625686108045440606651923845534431071983109500879593401459457270781303275371634919948465 +0387570468949867854454498257661547651091256802128345749601432500529628510635061338338862610989893855279059054348321900538486115 +8602609964344200881430293688135456245345432239342905412584133371544661893209862399991442865006457440325878578391699643241518820 +0476780634258919055579958686158323045730786320160920071950074973087521728400869220442510810405620758553467843454120670874658001 +4611389884432116865691971525430161735365815156176932740053507703667082841676421694566703484160697073157865364859324160407900243 +7848340394578239725877088114075619051748487906575872632940285638137907126466280399912812673908953355586620975122613901023009092 +9989300098497815617534003740634757576144040056489564419015441642507944260710401355749819839918602861170379515547533183230261689 +2978159531889967729748526061427217809996789425967856884930963977509421860822261926488518659963801830737928324899351448743506636 +7739373703546853709971163541284486539624807396328266253157406157316793793115843617866707276779926556010088380028395990807722966 +5262595518780890084621315703653759393744008415536932695809891522083236025326969192829600448033930003810949789949558452866508165 +6709958307107045609075677759152351429582355756238261042407300818561928024091909129169346687529817528488405441096301840942497835 +7939846806218683995681953891663100594898796616302246789629809345268814966245162531052163166438108697094692065750286916247302592 +6376737923232528630569024819762238692074089057479592609265302063789788269041103846131589957468529143458603367428237467202691677 +1164704135075482622437354706246898083995113224930951841406301203169957259318901636559771075096487643893444519082342460614778341 +7571486167299822050740707597065176588093664070641995447134130614103674235649546121137008292829783010870775335672457433796269838 +0786000364088666248224822824647357946129181563136369891309362023665749511277214956791187825832905762312557124459238096857752088 +0224756578418586950260891776975306306690545499717256072289157907934175802791666439540405671709869057148319796761025632922961188 +4317800666800783644393485986576298736917563650897164304571720738720115271226750820847348337143207594067034889090129746637573061 +4992035733871695023603260755794332051946719932307664611429871720394241080075351265170984927835215008078524349098428854934511014 +7791275210894880460866040577708447085440157107534047266197077167838868317086743808903866856598023308194718747427381845326367182 +9440053603247145332820937897067937394376812799936768352012215925676355028026697201409962671443788824047418146714929473243964178 +4694097429621088759426437344831944205038778311615485599880533035443309753228841592516038076111818420993913946554412796041546011 +0591550477943256220239415601324460355602248522402154416815822080226505160015237485088369250676462054718340071691237191715854643 +3754782628510988470286589139121850755775569961153502324797466418949716647077195388880488321524207926209705534645689175931304390 +4293579894293631845176289384983754244067774813502510037057302019611329180738850594681778906215406264239719011424001198377631320 +9407877792875650325539404181214900230679005444970369243950847777718535539213759142124477926938032002586130641694624396314012506 +2378295522687601357905408069624354294555654221781438340067666919648970200524898545019520189688682193849864964964428853699551465 +7923565501350073769051751525297874085786148453411032926127268603914648170142127584914619282268063761170983684501064629044484389 +9840829700300902841805899697228329095218443610475399628627070805326825976451593789370050027841655892248443960659103428515769018 +7131899325280315528312838930296738263099442057104969325259438333829573217299432626262718217616786664813337230788868035555543551 +1613365891642882106953566519548821305418260262459032678151796367238313756913498686906585565640299870573937524634136703149075431 +9289249177275626622021358990349141206245802827466686634188302681734388914438573274476670571970132658340810522019108097685976679 +1999547679043388007539431538024419806104005321474063902499212939182657952169737390109212206767202350076020372910741798343053860 +0796380798378956338026387089364560910203624767639137927998294096484424294854533260048523991232484163916572752125119143647479084 +3312139471300109845789877486922941094495320192955690413961709171195278169767812527830359720778178037462244636938901054061989359 +6446495700380140348116004473224522745585963020017740642418321329101387653563805874445580561212083040164396327873544239022001568 +8221316129071405562026280838341093594207054207388991782744751735089766826502781865742057843922709134454840546015717044077633360 +8884079984504038495190808918953678045353356097676798230492279289061735280457857280913457619377057884683523850527395740839623992 +7553178237304616708890828475106185796221210507905405468874982714460646413397054645663825587287675092464040250385703334315092608 +4773590965589763472535194103574470410955536365729704648777555516672917330870335268784268577673152078292448804242825553568245947 +6215819528050303270969774851839361036143312160025453398613079612450766506304878967542263517886001609950018287965333798559597461 +2170641880151969257726247627112079520687196427192459411452224132231427774555020194976266757454256306589376845325708061001674166 +0808706168526831621497924679232450195651965661724083005213398910330404632324992737223126731122399064939390308377239660504899222 +3551818900906904056676616710573047283299775444456072812898768689067250392820375890588789819477340760813736549269624861064751893 +0906163675906901070688176210778473069828249578452605511950224714556564390138087101450788624170738723043479291100038259738176381 +8078282667536765535434830984808011662048300628237758542292327990288591220497965448597597894673766538440553768610799486540893085 +4239348796188542060619155351521608241401936444825596707153268722768490308653700045149495791462321531911996176024726025953987339 +4599085925561530429435581768120454911625188723603840101001827808010649207159239374191781479981311865586606666121770067310616172 +7104627786436327756821840719455701887978236563257187943017156401785911149438142292942970899875188032869983742209547431350943402 +3524068403178617373975067637980111980036408360680020858787573852832862969725456560160112278450433733839443039332972392923338691 +7361417128175114993261450629674608141894725708171510038678649037487846100911817687890240766440318417379790915067480509929873884 +4192563045095005960884279637612799513276543500165975164906153877957210268650229031342852943158004290795716146485254485943610979 +5527495850605424448537975358841328894172810067249914112763981461492106207569150714303562508512966364997576085520454249415595925 +7749567920199753428291369733514893066123672937987655345561072264405494064545300253825630928142777746597418333809686090132329222 +2347190729828800374979423249344630750463633445579902195914226451238506679798785890126552693482294276302768079721653123608475225 +0152265832264604743532240505688517479981918489770686750786602858928368893549157302414453807821089110756269039234757397043529808 +6533482130565176488100655388961819118126865617901005108218775489706817843329452026358354168685608881349897826461107138466301657 +1298673036456454794414764858082769061588499493558362877721489608505337579335664200621558841720543957496547716133300368539580407 +6422724558120550176442233652100893764139597343252110471726917934375351595018103999311490022927771281873120822851829841201522060 +8271532119297676915199374389042651216528118441258413260870365096855873207948565556782103615860771982841168346920959540519710060 +6184875439923588759440136066343058630113336838104030510817176224460276957688775424874821399346650313045479118572616668489003296 +2897490483936533080760701381789308980122333755911815060695347160990990030052801534871717538234667743972256918252210649169228903 +3655303333861985799802708736697214310308014845445976122762735834979387407451810326226050509201808567673064245381176975037733147 +9657194682720093768010430501297373816646166065617291768151706574173498067197264972548166688571305758669319121229890115185819057 +3103510865220902456203344829140970552995511866958564974902239021728149583173073409170941518663482580344763138822732758130711321 +2350688930344799523350604942490602629624759567547933020887009924743050812335493964555189623547661668513628841842940808414417517 +7886476590659571641831046458439879022628434419829165109717986460127979081642839919400368933401267954441646768446103515126565637 +1110171943300926097143589488105661141081024879644220948038639183302967863702059520939356184545779690411373217502283638534283105 +6427345179711846867068806487631152636131101160079927489950186169690439565446479010213897921064107507425078506360801250554414400 +9449471539328516576998441231176991412965843783160858329264144663894049487064693073174535162944480509103362252131259963289347661 +6448977729584408680822098278848838996541448014927561863895692613238880261580223887644439627676547583170203035028358611234118545 +5113424393568423046116537671317616837784363459294529414397028767453793820041115557136079295159075170242753593066820395440768961 +0357417280340583283641050541304196888459267811242609025317032935293813444330705702581551660482757892548218860632259447912758396 +8759325176891928932295819377731294216097307260061594553902071464538665097894582707057397147529275324299057601238957576630914051 +7741473646777114825234899359211272805439289189063644808522711602498268477174042815308766568204260129647306647359885729554656976 +9309329125552267123498982148765832181281490321112798033562587148057414223743472931562572470841497241968346307379573830843375864 +5689803719011022683494640589564528393539106647783122400405858218032186185463041208001667912281578057825422723357469418158669943 +4135592927602852287576618966571253116928765233133651892018881037343848794704599894714843595981667962009408311650894622157342917 +4255899203623494824224271003677968232634091967503097880844441755720576147137952557139636719564772738964156856519335241825543143 +3626415788714735175257556917344733999894086186508542273673131627361802049868502277018604195271807930087879067221107134172074310 +7958546281467926023449343568661101832138815620051349600621939712639392675220715111033777811540451328852498746778968470255323001 +5507407940398075053567751242109569783310327198481187906287038704370431459121242392319816826310134418784694594320070284603132253 +8500952612124145512973435903996934578404485074378319984371754538530064680891506823775364453662506914230333187130946894042814136 +0265665148790173017555028894292865406694173385138855281737615651927882128657965562089006870021634293021436977249028055567123606 +5915024874219893422458723773331806018064564044582475437209765195603359789772265483078502778595048000442697079452809908661825491 +9974005988977051807595272626225779862046223804794634926553502843630881070875123298869912614128545778960915871587536819171698115 +4134066090737503736509722416899089791584075227377094759873530554067748069082533106208531237181423743510501202120010884006504203 +2130661127717575197825945847274376219456565491166146558949366985214290031939401399735403160414646904595040915898139346351713694 +6324612167251656207707087624536097155449639896731425226748776831598133504224173609907231060039276944120772510687900080346884581 +6779168745096711200457711019042996058420593520557512225066879514823155886446534991145884583182566717602534510076749929656585763 +2645365584973687585388329644555202088022989110607367050144776537693714417352526613252012599647340542087230486009799844093446266 +3564846704113295525583758182164662009216216712683832484187612989237709031101278092921642250525269665257513687371728284688457875 +4269555170494408895577906466689197972679804550565920918614809933468304708581306933698168463484178291731316450813136298166976686 +7026547610376955852909877173580002842485362728362791349023862313373489864426541187364755041770087263084268647054320360965911322 +5647079585282992247752748756022346611926842984561046976951468976647765336240687959117772104657541464074764281634923169020133548 +6986017692163536989579104384975828200486159266813636401205178471888697579916647226641294586286142850042407781409124499578432265 +7557633520998487338874304841131912835862129914096167258884138035045133191763280844957441725806050932560746430226219247549692154 +2059549946818227720943211781526111003981250924876459433602235222747377311088585283118957625555745513105052583294040713888376552 +6312790236548842295363447664682859100743206688687480099078223631017589800121679494388116457487107923554186562511797306252062729 +9341961249268388400507893558690543848588537117611908256806356375473026278316829039260672100995233732139958201591529357001905122 +7622440629036046952745648171252074362090378664664601861125598525510174413032785951294400218362705244805632366004605852186841943 +5591402799903315324575404129541681549710964981665090508369906992078809669693743879553048600144250375936726479837822625004211533 +0078063724382242661885925388201522364426479724179540766386042648425409841158873221737967588237291116048440934261723138640261227 +7668975572438016377259095768317171394189060199749631603972553612209575383312005377253743770387375624576792404785414546299669225 +6702311775340274741303788836209727482858012234498189667215083952930215080983474097496335600683835003845218991908331419228161974 +5844861014709438146848025778890120727743982063500046236695299463240688553674143271469625051120980545361183599015488717404464199 +1417923931690244544999340897859985367002505948231379127672256195312588278402509195472407043750082438215262579893876014989408706 +0852865918577980935645894147403286685964996381094297648429648506192278067863183038777421574419694387317777907505729693297708412 +1477876485508630104652801640230580883493634454785840756974806839665717736338880683347432665781835143575902380330089255654038083 +1333574195103318490380293307280457872559202823396902837942959887393577658742570521479101307298105809134466724699237489092865507 +8231986011655928132234768712876079867027010505901813055232448036254614645338353115984374755578779894249199293331904818734171378 +5291198257745669262198453729172996916240029288161181812810389503650167962679364773038871747909175248874112616641407342566234542 +9827656930274121781454100476261391707245667711266140909114058056141317855910068833712824192869630959336254369672357008292856548 +5323877306540282063144065009953089367459505720096157841731259977506672262970125862602619551379971955654409939177527714243403057 +3533286954907536500237907021493132000340867715658050587581466366920687545465498963798769329374590369604149184427380755601882748 +2772451245387121398103687673837186367345979531432828052838695364605611707914643359500610696276887453665009271796449018655863587 +0698238146791660163022661497107232088142858713995181413650802098858185207125972232613318849001183174321522406825869523281989408 +8598554632732565974534400037249824032956321514858885348539735224820260572526802963756049116642833599682551125312131721936464816 +8777244479719589766354029439548567013317807112981262942768196141952117452742612964786718669561362298781178596889656657857438059 +5923903176580798552139931019374192553986137461354795191944498128759243022494357593594711086051327159717361960027267213667050902 +7713620888965717050672864796196500874403820011858648932283473719914877636081597753296264071668416201048071423687724652867885227 +1226026168424754948160701743250583052114587429759744035821120491264623002950973529144113414105707200986835221503669020815368719 +4301615723202468195623453039744894794833018107887889921748034816356630393443669846540007477652862272296203141137911353369865567 +3701305132741529846291547614776780687562767296077648285050793745319685122361946265802267010089656480989090754766793951985030256 +8873911333836369977427224870430320356503521006657767633627578973429639446804587551411591770027775298708856911679457385890346605 +1569544828599021231532143176442191013631066377985711192575263159095165119051577468547654466026788861257476052155683756383480477 +1570683253965182842006010673097287497172850525327662453055182206979693937570550092128731857505346441332074707878996538911947435 +7826422261424373910786428920393564290525228206911026303685173218951588837324122530045948404629473803092607834231045937648966971 +2638900097751822798368796230240210897707934867384226561665436456581464223307579261127638844885560109562036790807667670390644417 +4192581432555251427389363044896326951734847648428400345150360499669512737809145524003991223977279428592712065139442479960445332 +3942282183806384991949767715266661401310060486291440972343510267352817495541237489262806877081824812836064827724828303648923419 +9223072015234086234796090436383818568296143232241955872923081786551697504279591818923391084950971313678554302938168053766302337 +5040295146817500973605556893314901715843407186937767008367308307925199801486021624397893053770707845409279496052311813731054362 +1647014082822566848815080638989025226353949427396868720941862068805915405636344175116957293867502515550210403185422186297987101 +2299194118400856813156210708210682041625725297454205612569506042325388570654000072012600316753710747599029554756191357595113351 +9406123252615695607363852828573549803486496914881699175306276755265739836369938313041363024110056929623864023133434302918217876 +4012400092952627260881136088985623398889677549475347950880378929158923209470729028820934081657587476645444580153423943137551662 +7552074556799764537114289715930906335072549730926411552458159805302034148895295264056331531618830126090300007948842259905560480 +2034810936658458335936482208711459668407533754842615732173788255099444966005654249703291665433879743623245887203502319367176453 +2055826854912228554738248289886125490164761646368783638608966701408500405439745351176591154838568604792386814062201460103196028 +1704883259604470302949798965476058273811653448846315941108597287428362004683627667421915628743842382745845960365444382205614441 +8276093407998769926806805283999661528492618490261728950220171409852969042576745011296912080496971648424710213822281461166346433 +7954055459730823465679899905176049956104235683646988019468094197473567154076726095028250041409673376986848760985312614035795365 +3541288526820871183022785627606776202868249144458419432875958263707872906136295450220559956136362126175679096582857639739948725 +4938942457166622758688764402917253914836669353169972138509951712963418233559611193066044779423979184123891151058275415387004481 +0186693532983400032989768484986211599974215771878040648121115240600011282719832395142348174280309789242173034859607865458058836 +9693011226500711160574222437812022981161460007590617976243921827767353835606291414448435195278815539213466555449489307049996549 +9509118537609881549110785870137427018215479109414974529881125038407426313589072435487055670013225428915901526956102119247020213 +7469585110154725916486236087220807528982687428584047123899577899339622871539124688449741918490207266633825962457784296958609358 +2301098947514645707894405697878233655395086728296961354861714139621104988822145498752087551938260818588440041204929785479253335 +7195002791628422640170129886701270065601981205842819128758172220230767271007555891989535523140754390046908600193906165109046248 +2903714800754690075212414752414239844419261581404644710369810146988392192489474219249291646325601976712417376991598429990116224 +6657841194866308789835186691755481781807363317310203480516610021219245717136441915610841540149737012207330154276656421683007775 +6496393167312732418345038041143115847682160487308598451735321809667649213873941020106124895016322422646066096706495173559340273 +6190352286444996698010567042083025249630998980474746852300834243586692391358290885793710964591096599205367697354040455225748995 +3808084112830212575642888231806203048547309288414193348028410780142000603799742289999737003393558049032105240979729430950416734 +2055001806906934446231057752350125761310442613239626005937018173389749155377417284662314193484961591256165582045826903566815287 +1770910475516651085946142895420353886980271498962773028208448011256576219469415630937607816862320717646276543446021775437024856 +7317728070174544541281155663261784538153312462326658626789839195595942323601274570763560759947639040733318592217361764664122267 +5163497922629734181478583586735139172337995385407882365183581289857390438660661367597010604158349602523776584737697559311077546 +1232290524053006728473192557288773310853272463002083064021874545276391652038777534156787659533007696087737660972241935746823998 +4820797373925070252089423136204599368358477176708061478789645953897583587905165644362770501568086914083487441612509546270070041 +2710844937068669762717114936914428075765570984673461573362652998653398467217253506007120061287151708786386990808903587499291676 +1722239692416177387037629004556867021409013954231954955946248469232703827805268278720272112690685704443311442687874485358911812 +0696442461522918873003473056316397138103158503920758588244927506442635208935867088943853508413143951321019830428306333251201396 +5237490053651269730925587281174140512120043151663340911938612562084343250088788816613613533254540244890538135550921235569055235 +3652159735685467737870844804342436795620955533935479356452033442056822975104591188103222254197338188275216408256009062112739968 +7134994798879023357613115067249723046493042657115745023241318111461450746096281739377465898982050597074346498759749836646750845 +5435539953004427145646816313257688146440880667736243466005411373421754461366941715230523658976477672479751504847483760926576034 +1189232066364746161715297654667683387924489405288055983423309925216635045159783671997425077719999713937624968743960815866938439 +3458042402161473455431554634378956977066496820341904422146434258908727947177474383440746332129391825690966243498723403254667650 +8583588769346353411734495459871858141713721510897174001175951449320360912321580651780695549218819369972998610310409490019288172 +9334204527475060600189111850610268775609506754404225935699578359127242186303638934810994186504201384645847184324944047027889017 +9180203944559928543575350648779851174917364441299737433952437533215087774057147807226819191868491441590442039396662591808502513 +2164065534579878437699757090955883471447031879403082053258427608568820018565147466627486801491876503507730663614122066321703246 +1913587358432681308736116815399127159387589682764288441702400713256228014611237877207779478537639968530764623959147519370506561 +6235255735630858442517033591935698933960554844760005512206225607812740106611830794710056776637176558829867814913080368920324943 +5410646760661322485781048142156629014814968811323925705022272475371023640394527122828545405831491808608741154836760565505004928 +2033787078461763925201661733809116559798197426225447373091087213252755823492809148960852025727421510534412088471340160144342242 +4053406317407103820908867515275579307068390637321468991384194322089645112923727349244540751951979469134053290718220589958599078 +3285805403257089050405641613610899794397383496324785340103859171172319440876039603408630922409574370487373867533449326075083340 +2983815868763252323303322070522064853273357744420576029234853609842229699640881843769482230526051119476446187157115583149336835 +2361402646872012433986880843871560611945683230864891757700569780134284216871106221826645632387373922861192629580123373279881156 +3429998671996373176825887742582100895464377776504251588052739302345778540285554961799280437565093922440989079226166655301999596 +0886198944491737540229136497483278400920360475905860689648388002698383026711966163612310598810941558644533048630182341670726792 +3224790998309426141510868353814679975103849323867287287851298934668662832191399524482143116612939771691776186144211021701423317 +4793327096193585617231688937441621332297850907599875602232326960040073326201864681000919760060752937975322888247984232424151851 +0809471069558103959275275538388584472907830398322459954494433179596574461777867507710018713571052848608078849868820600263934936 +8295056327643509778568235977901421687599218957335038414287972297917509776356464814745628543893936365866982215021206066423916526 +0069175389099655621827463771703163016234772278712340002459468537149630573879420643113985870282366118625694647368265400040980694 +0185330675548785458492267177712810772887558224559469934081174511045410475070695699952516120170917198086628352522241811643698589 +3432240470257950810314775679834800845473620978487244838484905235351184086387166599707527028976915030731776500593580501517198631 +7221196580395629622683771151068414253703527355840765726766691589948635031125477445246126627685840897423930890628894585553998214 +0691851299476896761562326144030082656664005132402228246129440862040530711922127884942717942941321981606132599740710622690829340 +6387805954414104944165651355643971413515987568344486496225929165033526273702557885861628429072595508259134031569932407466556139 +1855370060311355098279801028503699206359780131940779590143641110125855998712927294283001777864930865688001454553164651367990176 +3395026397066375956526282504048669461521946610830298850923781230518236171386053536666469499692151678856847055919172630657325178 +3046849923385707165827109497171128710135811605592578271326373856764418443760349195924655728319788343802029428671675457416769456 +5261821042279220321036999746346900717107895012894353289951489320917708733388530368251182473743527841343292751660664418023250390 +8824252317744508579970406643025576979363288136493464811540150480851485119103480579103156985795448940762007262203877811330652870 +3204055616321408250954812676138625194951030184386810613892670161995606664768252471283913135456955545480933087872315934450481826 +0110599619290339021324056958678493559294456117665436489672772960474962839844256174658530179997209334133515553960451214172384426 +3123165163841681474367010429491835679876709672710839983993762107872490425945162980229932665274102783110995818120564672709048084 +2232035395926446390712487234338471590552947684994788621710474575654186707905435367680711475885293815543060949577152083169955046 +3350028292251658928286123588620947938740352179851517866242963626384508167950485172272074373493999323206780786225244298185666999 +4138239667749081340443940138823062129167164095346506708209137890234837357780622504863833649209158347489730999565991588612008781 +4664217286110698633018092758084011823559493288270590581425742146586929149871453186482768470820080489858199024057046533515257475 +1359038127174952081563588721035499113638671173404164924825530484539148472501653910385502837523160916070082537168043390775386612 +1436923728210391122200372504366645991744778858443244990595402772009992024820412617754275410349299293608939703285522116131987750 +7070475686255460177418547021473687579971194688396164244442299931146128832803904423667313289561781005593542663645306028982529133 +8234121263247251924212841508107137520536152642698580769661242285329043039771966387613256643214231439403217580461965950616775878 +5413104246474816660429811639531866004425878342745986000581091374331764631199000564830359002609727849512990017018293163945873976 +9271235506027321498393346844536657245544343198147802739604119444238511848559098921026630558834845589124777635275843923942487146 +6915026105020117228571904488841353205308577369931061433915716936274811913573096342546878245443525669417786920642396110509919680 +6739657651695868636149376334284237799146008295879768907577994879004415279621922483186516644311320818173589692203657526672858471 +9876510782436209693056682744986249105883882516043932138260023370268816469981968622627487020614107433825722570906101692145161092 +8947476576936475757290736092762279169953016586726757242391235235637432154172158901821960951603571049760362400377076037216546585 +2735502157822565784279289258228694712999190257163908804541511707522385665999422960150232894155188313507493238330302410605425296 +7940101946087505879550405973067848188833812225296523952905032878875050817047074123446017999571197850004230072472112763344801201 +6857142063692961406887982913011490273637829211932192499375592176104274764580285681098203860236999583984675192011342612589287803 +2211465020826460764614937570329924717346527293528447660776999654426823206473097384092520163892337570587601913303602876478051786 +5272016532080322083507311689593741698084734458418980219100525429302190098183573551213250917695957795596851135451614813138939428 +5194225250639639525068156399214932189538746207985167145356511964074580667075083175359149568969997574350520456153735083289658446 +1455560636188297319680331471565963722123437735473357325582592487841750254758281060767877781711667628353861132757458093517522296 +9590937580052253974622159164858580322392765535712215396895455529996073185392085906968002802221878982904887664044332656986715431 +9536212991471446618363328764237863492326635377747715439634427901843465449556160996406079848698687609807559175166455209276718369 +0962150554158891455114948237812845566152678283675857088563676954226626094608713709080121062121158536561703886898127526222898762 +8414892749163551177217490205399965970236826061221918459810953818518538232318451456451553658102845761329243527433596909757880804 +9825158812532083534774503272169600063323718458433952873310136391419269694767849055513012348666684447456130828029837154887287827 +4062509382360859964541627147185929761392475832974190883249337770970320052156457280268514718326951417528373787461693447362886601 +9802384514195519930363615938594105163103928246792737359775050266251933928460657553568168049147890604569802307087230082628479583 +5935332407364708394968146809867463424837289762388992341535259500067286459397511377634976747563308326968703921946360640378185893 +4569961338778055670213869684862788995924625160473819440389284613604453202763523485241482845106528449769880720371645531620509281 +4202560683681703431369153262516802980502196714639887848169937613326674387122996761898343353693436995583214395894151975156893071 +5332713363776927366695888962587179393484799105075109946348286416315933464459810857879632369155531524704654845441581407617245460 +1504145331569763505220832523847632508529957830567158287689592530569297300966404712183597770247546234814056520028432048220079902 +1149483717724666058290549158624685899733135096369862966589441778195807957291623190196664952975922610237417399523101511538417768 +0036082673459241242094083136368293175771615652653244493296672289353318399016588588663275146926170769838811055155231491519921092 +2671555423322348703897548437846131134228893160392027737396758641632859270434645917320325207654186499204544521705733245689882718 +2041837354468721403869880661989984614132154115011011100068745419042193490192096797533733270459341389726436281948139045659902298 +6287265676207191576589342058985373273830354712920659876843903556929428288343172794038913384089049546744993546672999661244015504 +7671119447680097893124176344032941210873441607294204020878536819662836852561652631058964593184756766213107639235733279668108014 +1503597944873319896726432783506968518844581130482208284211813252594573116879179483819354129815885419312761420721174077013349076 +2090575099698794920755209276799477214063322134870825766478922887601572992873375806104725864045048652936083491870937541080699093 +4276983494146036588052551210478424032928358829122542012906258904015903858342990969060460111480318597431250609578131562003677515 +9113189915457840484343399644582165727880755692052210886524470220995792925869536099894161015143779391596346691130912281801612173 +9319631208845800137369253760266414764140198516858735371828537522929558300406662683159046086394078157278265484925694452296859315 +8349237034176302850857262350204638637311808275176159745110236526981407472953488387189243977498778789698586980560479570319641349 +4582085067641332005668306046922050438876194786256238539902378554139808224133740244623740910175342259941720827473284689401445459 +7345697903120299815105938454414060402797077914470248272961589951135290709800128402376384567445478103311791196342258909767408438 +1687450542289596593441156541948105976556022804241843599203672068561696047317558154471118174352593930389361496742548511304322359 +6498489819476221747377877067530863799486747795199009866503783070729160667495759546595972752749803505545796148381282603736838562 +1116947692218975950285800049524721556053629253858269322416639882955425601685355611062823222776368062359326543474366782696365362 +1876190167722031024686917347027779070009896358771654643412669083054810506078866218417271851286420021860907338434817211204686529 +7526344148737332091666585836660697055361931941977353720336283268157952341498338628673162826343734542380046878989163995116270895 +0881257474139601407433704222324056930283355591807454770810061687372318085122818010133000433045729252354366007849932042280735696 +5414676249451280636930231638752958380804768673433252210159619334608464253621441055450445428123593855381938626029786836437726558 +1472864902858611995686957469529408189014561042091105708978737612763064100403995245584630738467933358860436751349270719679653915 +4154741684647123464440327863242393005522818718477064168160551024239993656696764829360125195215986845682299044548790324197334138 +3620127277527314655388754016922253350206555973782560191527034271929722748456430219013528002841930398470078939445946816067765376 +1722243978849829693580351795930973604730338879610427564825410468950337347468265193351290232895574144876817631258161159766300943 +7560827667474386459711757237346751568066838129529407855307314365209698298457841298119589066095648716960974765231576411494919727 +8647954673176727645450541867878958992452684388452058871232181090099976607265059152633077058376469186824930991444056731009872982 +2534991973200933430036026714058825873362205723979737750638944916727241098322793656977758591967127159597583155643556576943448135 +7155096296084695428953865082662825849460303226866978634425536379072621988021237204615220061198468810868414563936757985215986294 +0328355158636181420325206501518296045012302259181045466662110690600154986730477840865756276397313811576274178347247630894639601 +6488666618858920723988680761139802965152715427776544103393791345032803457282545151397254870010332315293514763142030945537580212 +8490792796508337321795619849060108983218521200268617175020002271232501077412217793566906763151880182142093233096870432790215644 +6497520222357920618391446425720257029473583117356819552842063897560547456381395533477059429635070000008137772848031720176668971 +8337012837324253697188573699623123203170708166950795276999966009577648906818828342730326847639721034032786616971040641824194318 +8936005757600971054320025253057644372313238443033194219596927715787049581460393528422411161050154583706800554645719354006707835 +4215727593152764210346931899527305466845340439034432649923099270817579403876601746194619933095404333412889852431784452755466843 +2088097487158522551752685495115047141137278634729729587648195891955992392424399063377394353752520259570862609415201408164480501 +3544474831228092443689968228780611679861586419457086393251353177701671796943515744580842421181721325756988105402798156643588821 +9778536795257051681439799124663145890473401144931092912457976024515106053860305458288529123695426810921506064347775651216836501 +8629179794286699832017474082041208054529672919556607496647432513709724969762646139135868191228785586347557306843431128687158014 +7106456703557605702232569911033196736598272397024373712296482733842097318351505429979256730289847489156750506335880609744614597 +9660558924487542734143121356331540689398560467085012517453390341732092436780513934245365179678624863881161450471818695381011735 +3612453516034902143796136205048511802780802772294479133906585232123112871839607661282061340027850740910859483338299107807956460 +7138724118583930096084405139356471004624454913317962414308274669136174812224878197906201370412577330455805945128690650516949333 +1730291821726950196206552159924266720782365385611242853448279354949948923432277012453511172303822029259754519038931979427529263 +0548575047242616318508715072515332204278272517306960109310804996719916332590161894788700070468159978743034429043329057033741956 +6780889499085964528861899269034340070621419803779139024411147795371750105312955245739852543595775978340360773771494478163557276 +8779458235824687171901362922249920581085853186181780124486013927395708657432957030172044779093473433280766667409407392636463029 +6492579411705822845905066201120652601308459859010659739650561022388466983198156117882306744218369432413488854001765063909641296 +8476320260631331607201248389753502857961661494677170435281685503191366884817616726638971291309647117085069726218509290967362859 +0810139343653776833125734038676243930878414416044335363929863448842581458674823745747264416650503948061405425537368655356993636 +5246772266327550199529567399996744188644213516458735587639817991224447177904904043235549503266714688854194981790228117033924563 +5461999291173972450770635507697104176866762020802521790899644748842731393628432841194003711839701487245460386154522697276378209 +5508101058683508523829079387008807816663732920071304290119836347773461806818814318630035261732455767269492882409702742637950576 +4599609444549117705097045766405200220153090927581307100167391516722145681413981100426906534790499705750570830947858845925010025 +1009651634984508835728715571045793550385433404603846489058904184111368085059460823145504327889561854662565906194985160507019989 +9357881999324778923494272780697498659881414033472270442304085245770043338219929173021287371666801815069695486701740310076529915 +4608279080766236606674170110085478654830337635040537744779366742642067739517988003498779451425063495418123468371481260706586708 +2700839483986172436829409192485873721128692643705335631914640430691426436305173498220746572777225402080014675196382731227699368 +0086136702746607717078625347234495013215002090165872533540039551264212687781885399476155438281141614343873696580481400589706298 +1705902722746414058890369373242227296762941970919549616605978259694120130539911344963309277369189903126883321693536398430654697 +5704737347400443449757566987270236468633966314353505101478851263083176174429030344023607680553018559157482487272502931014168203 +0929253853547791644998906236759598501768018124990991199353184644473283908513254590068607924929473380232470364424573291631346278 +7114456427076321654054593005258016944651062956332800845969220254804995756234489111294820165405880256529733135691041245584370134 +8553075127747556822577563288536391683930679292906640525736392953226223500696613649419343205087984527799731174525862937043354377 +7195787380049689624114711568372766827743368052980829823446372539589421589808988393401756593651612425875577606936738214349475949 +0181544697505469142362834711903740140759734959010974977126438624042186356553593244884210506676336566524109269413687671228874205 +2896686735795141476730158554607522934103668703053995570782216706155323744964613382384967846986182100960586040580173953854307263 +0286768549279297394586785263108063892918942908459235186623403809969114153282154910435594392237358132752498428484045697160025994 +5955615832427009939764606346259378692931215536161617155986791397037887173705219219646647075071165536665651942885935717666403172 +4276536421804556864922554676992233459439755861177568827029918375612999063128068548901725736512083925758415949098712083242055096 +1725062474106111957956927114735687646594918046204634711666975260667778901437761819647914937715436330233142512856404865653199627 +1291532637755334170692403451392823323540177224847012033326608939686606026939972956526341007272336243309559194347525857345975521 +4741600004754011316928740091902596508371266808717732973936254941000335221943249074812743110404969466723162046697491974991225004 +9085942759791031775731739206062635146158396893656161608173687371708987211298821225045306367484626014700868922039873009889166761 +2541340395518961317950998826785729636172355473248648132541946680948357368185423797382627281741493372551999786014593359188997484 +5815863362546532185070737662568520327034674676319716730206859041401224197894669598892125005912741187360523778711460345399659500 +5019261584110255017064950229895215051849707546749811603776445037494938571872346104053773024040213974077890562979334359709174595 +8333959703819799129975411673347762248495046754138982930023526377690018754531518740079491735091275401960400702618787889510076010 +4581333314409607030057543494344952121287231730028937956224706941567517833092451729648212810249480493474654543033507398453170912 +7852221502784071535646166335599143402394637641678513241517161191111016811759837825114453186940577300367319815188600287295217212 +2010730508881189649006207896145313350898015140508068016693226343978406558747353752836658017965545008809220423091595161424950648 +0496642867065050893136545829132837570390366632826974250747266589629788809643453838849392317508541173149110672931572489980731531 +5044656574615736195246481865299114510211631762807229391764826087155637720268154669986339615396192306621947657967460512382581862 +3895441416167225879629472693046552479446299590678533650004819143386099678646479751244788463147638567925169013110241886917506841 +2313203706796269432481996570645174678020808456857425673026366797683548661197668493887407113195800476443548512276825541513285096 +8842881585797604763305986235159142379357301472286513544030211227645270193019240287343188528847284806872333232004379253392212209 +2688409746134847438143178885250323822851284371547861492401669288415720100963691981973327453125586959600245064339903637953701615 +2157845883601129857682095395770985605599007238545319703595004924079346833281510603116153010794429037127659368861617341676777914 +4401447994414721274888256152671021736338924549726141907269816501834171190194745666679544881448528084111510223352357146390678748 +1598441987414496519030297478102458680201205753852279934039229453001433839642149793337137629917651258040708764206181523433254292 +8502235049697025555092849971402076674867464110183607528142090872909056796241616598564935589465706229822069262366265937628292137 +0055816249709410284431305701461181450452686881595061600026629505373443676670339321519731346337868758756244867546936430600344835 +2653221234894262976174352794968903515069018957828772393070009300269049849197795885320787480595346792949215526978359438371233230 +2573198395480105527983836130766181207518943018866225650015829519188519988960108721260257064720088842555685468531102293009893766 +7692694226464861550196190446675400547348832497307906496613188251375658889565533214610868096123194257835553142178169804139054021 +7725637998171173785445603097719118044612631181543666841703322233562489202164880099839601175940461517268783567897496005081890093 +1623476912740353658966890439792885600281612076194740147557413318421891935617196959684345685434629547787219047616918564873155095 +7025393340892141498709286165315521796432685307146924561201314330411100254047950443336104515273461423562157427134079178986568513 +3037051173501455568382307906695636378996155638857317043809314274578391987572790382804869815651855972786188467330565714827922500 +4074893387854201023525918388726764794855730871461446351512218023803177488574497502040250654764531910469820302411970861290475346 +4505685142400586418253034395353236203805450253479017801928704774791824048811941055335303586560686324885362705510247805420001343 +3089154219080968310703760534064569831009505236913263817236675143668487294264949278958590434251398242365729091805726174530517140 +0036394729791915199963247134854987262729782792900430729405932713576759938708525288045097625035200406357341027793528833641758308 +6352067888617175431411674423431768301646476262241768575619045274936408735985350498083851711439785845206739316196883719103192098 +4796246905383107193048562525408958439687461541231738438567101188983088993969984494129981508412118318349184613817013959589269737 +6278309646817389075935268193490507135536415367887824086765490270943493340352420279233033265958209197334685770879485799469719026 +6501644826983757753799478095020135205854302975462551896895597291920140799672078608383168757287535179491461326280401680688209269 +6878526040144420355891683769364803589616260114471471935609824723068550467740012749917856656207461894693384857525476785223522600 +9913533038664755944016342494625355616351591218888135528544319832829890186060691211822484503271002284261557646472978467535245524 +6548210843247451049422320528853823425570998217119732063096360026534131251690098102596630502775298567714836475115745947762170767 +8887884056401989307352068428168695321737823154308274924885994114161224059109503941221400198439058269599476104839310697936760947 +1499621302434101869959148741723191678491689696644090274728115214408462354277075304721665902694379966811617179086736989510478453 +4472180192689505631132593579798684787021607827033405644669077356547531106428251322335834054519860462918400592332567647993370324 +9048351300356674084399149618697303904545693491523593398209411550081390607196236295352104843258693200555492607047645055200473684 +3904033119579621911938196032662442953927339169769049079015529114126726770405396769752836907821633197786728940414757447592836029 +5698177775039384194905994353296325732870874582783505440992449252246418852133705888017732270642203555475460239299507823556669568 +4584762886535479770675275444567493237009137069030861149601485784515963297935895153471843170037858613325007001549210631837023209 +2986230798529869904729567059889517914570160421964137879893497645563877292347055745496295096825111567999357916375574494934291205 +7206470066816931559279891084092643447735267681920960477832257393262206090265312234429580381825327805715755696980240563671249333 +4975196191021868367044398019078519059684387790904063324517862970957096304027938042892083911602143438683752393325035772333502998 +1316065614559478287404397163400580119624302865064035828659428240183667221604845265166461282189820016614382937134407772850983969 +6172722824726265969836224691170018753182037736393837492582066024393428721754417142086323296047227886511163988288746175386143366 +3150098340574634288363424590978979671688129464997330011306067549875063142531548626552657667362582081599069751262734236839198246 +7320831408674583797739325661435931815687746601771458516299735930183747747803369077854960129285952199586802721391111341133770000 +9781649076575876416834626425635920411598588075542620127671675851163249442163030188821293485762987388143543674662920406406559708 +5939935563329383384304248329695829758747483676028520127505271048590567171170009956748249750276404393523654234793402435394531559 +0252668408043754647206021991183069125065854666835585971567158743285135342908676173306885768974453965943775589652707957703958897 +6941205372351396701250517551254482102589310971175662401605631499693168133844709297036538928165453987035113124871313193061665449 +9276802658913609967848115344605249848453532699908586062377147315012593156326148344297014110306443933718879254374138651819351013 +9358073205389217401007942156414789371666731131537345827743121944929460937083473364444624266082970336996459723671391030112788573 +1765411908158890001229050208942988203575712050271091972655376675898080722954958732806828573013490785204118792568609729781493352 +2380302146221749454886974990613671789801805162408638634096879268990438108120689272991357553079627023448340790990962415597084280 +4048096687228528057040549427950909321059124104059954691594133361792928522598487445722858314871353602305792037504518403367795101 +8791402885580812697264727719699410880547796108771833960539902147827417638408675493518018213254819498104814348795878620396366601 +0903924261354725492168290232167217374559865613331033632981304605379493456181688182582298294804821827770454840207003173905889439 +2319227785834038175693137220680318778454701304164120192954225359327542167556566637258412033259709183354710843065358146305555525 +8673917622829080548174920911891769587355766435165634787328419281721579009044997104872530471989859299305155593643127267879722422 +6682290955279007939673997760490906289041187907827046608623932479656002087546101920096135789193088482019418559812270396482968185 +2423495240985101912282160197226440146044901932777702865325863608790152621900419953459804469566930950992382020196553029673452779 +3436610407681610128962456711300906610774647139359864663150800708313215865508716982334525964257676579485350065609743793059790523 +8492723526317024179966715344536215615681812917282255110526575184184812878258269018344255337700911624857617809339928700208173945 +3637336636381056356070802698487403636259922727330027710606038599582455818066875278487848716217844022297313070587219989978367370 +0060775392327016214743079646392570709223503152196217613938079763344036433088567530057235155273916070612559581478927578508356337 +2980025169096765222953776328674278356394573802596587538549701911336821174109269755308129020228887624742741743790423872778133411 +4050821812671715448039700197323622949979651733365312936187264385591443824448095216098907529899820693216629857601750862463957125 +3142033473651787251598564785166706147991399595143208797800693772522221250290172822723884818192653997830457442015732513857367482 +2434227934816021841461320189923052035591521089170168079786149675686974463633607305584810474242944486308070058500812306124076610 +7715099107942733927512136192299373968619398158672679676087995950860231222104112656659877314698556942502196127845551909712373108 +4069114461731658732633727592893010579600135097271095735648535688306732518715682620335083055144068477571815102417990371455419096 +4513648336048758526599148974167943082379478315281923441623546714332454634302169701041016212788185513022911448815223921787570792 +3624469067047527512170727298754069896031789622645772484827952155406739967506695954077541651367850929296976343602998185574427928 +7941267068022945446692718780586230511573764935339043083147537297266971720289420223564631007244922385010298582167405543731618295 +6806852318072988521804655880278600099719660787055120319799397720389095148995402664025461591174050670273042539667806005860061500 +7207008027571345636661751333090766740919387017591058445443344334627123396031143577924484316316865139392646603641126628696936857 +3077564139261564206167503001531356981773542928052096642681673223984499732941393025220000573381152245673838328811500670802459159 +0948542799001533682410297780373013799791748346265940949566539377367731087302452174028989049580452869089162990037685957268273937 +0880681561126138584261480022891283215404751141789014380796409952816321543905013512146286826522138659411899727000462608467450535 +5880091566261256557011783504868257620770129211977999169370084785384096209807922714348396458806443353193445034311369641072095970 +0556656033549253864960039890721015883335372132680590369609602226419085993403091634173432095151442616233244407104672122152816903 +0104165883408627525444104591380562545622183558159751831249938218150151310276941351661814981521482974647621742382874619909936820 +7313501231477817301767451446702735705428503662442130539627888646852578831768325107739301527824363969429357121983438884878755269 +4393819179422351389843662394835319603450398553466130430331252873486799616761474948216299677793061438633632338236018231007238273 +7068094772817742176160590443426997804722265949259567119852638283512544860402755698118178658952516755701578554245375512654131546 +1929058509678608173068953824208364147161581285515961193245483567752314654578018433833005722739540799467152027333949229966280869 +6336730057716861143854645697201406300653307680945816167555425942108475384088151314617817926634797063108952284581676269870141278 +6513049997339587705695549544072595339278305253913271901919825663866146612507671581432953692087570028664604274026412331620371074 +8287494858287111672511595795429201499531492727612182441663719617940686643391436311349735908431637719847434127985592417043341276 +3683439281027866311263196237695664936547654237370870765012602749829825808040000867227133823847412406422822451359747985192576292 +8080745720999988790691750589159999510681935221965279452806039652949988754070864815196625235801191787296756531552141564212907151 +7193959478339407643118308465039555507133342674876808586706138734909756613087265895500010160295176435656450676824231321652261709 +0364925287385399527025737739685585498448664221812361858587254459582955070942164513804426958941634994463722930608065187062143205 +1510248350334956246451724384468817066713416535078509857129529545180481755289335535116009464206553367625156566677354615817653859 +7881465671702288772143540937915254400157725015862757578041600213873551218117006116952755764443062859739372284691117745149958234 +9088672142145404730278835893039966187590126325943635642412358143475145374782213403246198035026788070070178049812453881844272867 +0338668401687420146135473027030982411204915710337406365921420685513130184588295184291465724202435316806267038332889397443572598 +0839432579664022013756612378573508309364105293988945101718659897497738253028464952712017100629220365209543091962896609529479679 +3205960596777307708890129013560513946330763313564335478005273643120628824046289255031118424365183741647820073945909472936879735 +4620269579622752421433383654982653678424583328146692271652411473449919421964392617935515652881545262048907644967697133592449864 +4910971218311134291321139032124590602851436712963785978946343853100496072075735946195731359706831946181264874128557978861454843 +1011713617018211148846555458691095625992954032111035185714620653734574566700313717404927859717431081627049246861417661324499234 +5786110524543942705885896839017534087003537095772880970962648591315518710892525409209474091669813346069319857029093908507508520 +8853954861830714292944628596393329384568570114810794672937003168548384708307820733820193722197605410037476744326684584534487320 +8506177470614128104032179365484641505802789779285846461790237297261018747968281039195118146388862647365824837138881138267868859 +0627387322960768524871037409484459085875896325439489116621528742302285368082254073124593119410595219290194886865353532781155425 +6809698357496328576273024691628485594217747112697681983376999200098136183929376787562599199562809870612840417370015973807170130 +4180345838072561608049345847198586105129570485513244519517878528919531649387592676465860801665435795014078048815567745274248672 +3354246532695167434429089502120887783749962531689888071529106417054905309204892456124468634666345233412562908228212778433482865 +8025350198646920310690370174859009390241456508358264258699021020277939851249666503547743318751810595431556900870080845029269395 +5024287042925036023658377259430412863334903203075207016434236014370755084100466996153432751138550920390967655897565334875107985 +5140055633415509862050689677756134057716739901647883241169162784527852235725792016297971735177502489661133426961482215258022336 +8037402896490266221826083726250897311919802495381754225581047474087034884706618427848292889662240224463663278752931604522244404 +7043755425948194930791058610151488175614282166409617670305934153837246327320668633959913213103337798602482509159766659520973014 +8270324793209592396753604357772106280615543278837901728489074421806486555551552367821526140160754356205673704266414947144922428 +1799358966488711764366695558613904123247231097498677269788824167860157320461004934898696811210735916673092405907643647942161633 +2879051334510139198581358891368404052755328890835533066541217913121822938905792625249238474950146832344471977043224365781135389 +4792241156268785166424579695585412116561708898561202729978503519790831983242726846466172154727854320602695467825448826562117055 +6892572380716177090374320438112277052543379149328749706362112028296715205065873069869487739216648881329625482801871208985853567 +6373214964682547847971295488976163394711102004330268920420740567984995588029489900437674583804505907280376076761512694288533949 +5155665472119733684583254908202345934203146938766729952819115305923223221786312083916415057710647121628060022705929366547804604 +4979219068691159117673733260174579718275355270622127824048810725346461586407079827993239800649367405496829848031012026874839047 +5453326819721439241285068650662264327283806902040744362117833297041970217611203752467289267570303110513926750426867300123044678 +0552826491452886926336136137608744868267813981244854176639767832954709406334434073383918878421433694185303125198608436174948723 +8232804838617139276458679276053642834353480357742672915996943482707728711707722990226821986645110256211853787080947091570985448 +4530019597199416503606666321373773776661468897805635174486648284631882284913728030703703554479870389738113486662451982640555230 +7993338222145807140991352100542338400006637591961344296816299971822252698512532704506078851263452743784059409504687946969377470 +7050536882923788373466054136459774772814292349043369279413441771827357318562815124739087054575308713882488227553010491099772197 +6089909596225409017825449105685851204483516303044569069782656841637982677940745207225513250507661349031619934115869792030545534 +5623878938475449105483656355950820776480123283684344289877071474158657289092937164564108821983834915760732145489786160801928029 +4816804046415869731980026155380698391293302660706933603124933543082802974288753245590234004708950597141938648083597306991959548 +6285742091945356496670113754904503990748375484181907762641311090270050878330341468974330973619534202556739616012441626266016429 +9943182264167678058972498083665971330560261159232616995578303066019814909746442924781284268163340228187122534568268386331093065 +7152421562633124939028810156815250340969816324770306713467508016775028986746579511353296216202084745909078793312626258806625843 +1270211232086431110376182218366306020098067244906289836262668331857252429211967620796216066094322098532350592071668917821894836 +3845983140880841145716906938062904326623489852177531923802141898083667738472813358868563811996856141869924840103531518902763682 +6718003149619307421590918630236184183948486678786725981990313301354204177947073223184765980604578217913950986068011309908008603 +0928467335337181093723613708048071495790652643180583079182628211576154222901059273081503840088736880283912228917857747961420216 +2166551801482676965109266503314605693500879290753119500152110927158165273225738493344045059814681043424105403563256277265130147 +5602984870377735940956944951929383106107020907201382858387070933315397423879633043403789726957228735509198228676824257307906832 +4033785332705686886894233935205324983504447579271074658738235607798108047836471901273736322254888901735575902110174004577938609 +3402493343952267342344754443371491773245869528553143427384549726736187617570093360565939837511071410896535571400200988988962479 +0467384270228802505542012448307525976842449031499613137353277852822620338697586513075361354863064717799164245330603052561106892 +8842259346260183856170976867586760896719736933276441816021638614978497367736310097708762890208045248689587229972592272876387473 +3259167318440772369838882122021007960375389506141034220872992638686407702750133638151910546511598120704345383522282582904780462 +5442641858108090041720094339574637116216554948307838544047344475741751117118100567565238848037532377180455225898883679663375089 +9577142779120614628627441392665993338561362305904130927688924256501300629907368310744468665785794738752761659537261334710214003 +8036648554081009787955952496618722107214483438085877494384608581966978660817589075759692849806559360759821555215575625537328994 +6018332837847583031691963477349319171451400074031373390713717042745436212897138228273237021196246257873177739985799453366628180 +2105213797394649680881845019293842601907281739434203168302170068885354441461289080251599722778415734193180238772976142195797481 +8846734820754645249343903444980443492288225807913782286509632765370978666435328327850741457922220028478728899676269184885886857 +6241211337754447826883159708571545245319208827655835686933251362672810423079630739434073408653288828495537708526658804579257936 +6272761641480084516721023070052621557056643754854336674557368573875689625068313256900499409409105292963555938180247719262631692 +3370472297661265053196085477234369632895929368519546792630928565306505801718550038387422423219452220411081683199640562587649457 +3951837878713231474233482132735598466639480024677257398672432796607337555329635538613813318615303256000562507502749357845826125 +4701181391450803850270234592090970559917694412710413114961034587689766035663731463069166460549538653431143701866309605231883521 +5130066101866721176356898240445132440597653755333619647577387320361484004480200635865792415170670027337193740214918631143418262 +0808586953125247209833845490228639118624854066699258124848785165048528440583631961380212929557758370252911013779599196305478822 +5521493745858166529682225371969453760228060851280399420192708176554476472315839318021751905690807649055065100259488774569707365 +6158697507308922777896716663452883986126333631244914987739404979553039259206951208801722656773710392910263349735278566679339104 +5235852402635719195587338866742022182421541295397761869128302452896058848115769056351269147058952464962337808384290518791915445 +1541410356265877277120453080537595028913680044162125110791583969590059857305994592466475393255534238742387386696685456308459612 +0685998205305456113478494375669069867507303172663122574416432766684294168665147860680796606372059541989820238442687325132846277 +1106867923762001975234815885590982053876420748926951108789589029118783643675464846081587064829483161231369554788911064379449191 +5812396650186976221479400172298188629572727898176974211003232401377793666992976058114854815038333629709686365618467427463150786 +1384329026943982531591592399085803625354634016228493888015194258739954993856952956641410330220082208258103940975869674221124275 +0959625394390538490258630101489160874897384983747423071388118372125410613424612801559234232776779480366009970965890340120701045 +2667599636961907870724269931710369308622554291093494631427897956168577372077593727180133140549717498300507170013965255252092132 +1737339475235603869117715688789198413446311276950893765017909814539886259628403094769733766342691082854440546058895772248646612 +9197218961185262852748275379196855788331061390003979168135259363646749693751540875250641291188297420332987544882702271806564544 +5155345846357272505381130365306178806560153732924148036950818502559878423925856638459435711194956693467002588044226649914787035 +0339515088331009475687588955523954567747189933073011102332237830656095392839856108022616789694653613303860633782147528467869258 +0130375935586298049775383191407798098669127243484570035128574481514435908283974501169023065679639597582504503336586846159045241 +7077631278309415202815413204154287337150040264985085284229097360757947744868068694211304208527582045828778373828218901884299318 +9750268301116713110151838109753966293343621088077855224277949045570521139532153236249008822916991091102238861514323117478408697 +7155195645472829181908341253643812659814915997068241585467987778740169901860636348467280424257966646686476472265113081410661304 +3489215851493494198992712587668351276298911212514851702684901497030382245009336388185220072052801963314343348536393431861793824 +7433750102437336219240711853055477575713689475760799540383502439038522399381791672647025184537779822386053913810580122038174879 +3818754166719135070632269620576055650986227950584996452818776818068696309115722467830916362575260788613336625118633863774586835 +2178221238069038687341161131798367556148830622561463128345189400786005116148867892392990638096974925106944221425159061603022670 +5524790379877915422325982255364820388029995625235481186060304010456803568052174332647747736403777805245458180638361448671813038 +2561932270943099179760490101865479784716200934807733561691561384108933097340199742932516818080682737612322848740218966854945307 +9080116601255117121383041648878380955264484146894062161454022932953200141179550531168588724355886537447858132714146647197563370 +5582470235237288151308759910566681678927176948236130886004032182125845977161600117740547183580925791449145036400529238606663420 +7837033717084727015977259234033008100455368360038870286352550514822219714510463917848677542164460976523276256801252857727025894 +7130094419267655355009949375142847444276972876916939298289507233195397404513990497114810662137361054915518609388753530456478940 +5388394232615483120266638817714522427995662494049561316381620397442848359037023023824190702655956805505137789130807808295750670 +7728487145979981298300914926600023112696314252375455071809186812868701327870915067718357028716612327551252066278398582389353164 +7289773656445897637637801900198614044295973498998341687324987147175709042706190580765940871231724558042584698396691392833921161 +8657047381454227627618374146397069336202044856822409754902332810974380485722671719214725867693990943072348305834409116868276317 +2768386266640115552548464265203598491327266098674766891714414339959283543826573869249667218521183972556730897770799418342118136 +8189160219232646049490114563802577964294955901129433201250836705921020927053769046687178122894793267915461627258608458791264898 +0219476902780453916709467789696795567878369111867867605857135264741583700268708214119151342234570678050700247598575853332533925 +1642635008587172832360657829315478456525706956941480551250122576528755981779071461911390584857611720174162647873804473942063599 +4967685799379026281672422049403080588839491839093762962697369896023527084701530700159221622072624944257435732610627292851813751 +2044450251147619884660437582513822729870849508689036347657931033634388064328200140554626737296767003665386040440053850111272335 +9965191238699562685527207327106427173242624391995490667681457409129351701642801367806048618652102824703936861574242876103982267 +8167136500949575593721265813173669081199030452059803613088300447991271804667292549385675921231605005292459008477827396828396980 +6509840801282604124983554742769070687886591529251914833953844029743868619125159895774188014042708540332420119945175514865123729 +3692321786307153306613998657737637076488435978265420180472674598878806315670458550057067811482012301407908299677656750462270208 +3553454561634700149917505664871758279230385607072819176680565522193204640320948453398810380708601224770333475704117532694153974 +9739801831118688634434296635463933483606562392700839399728340475392493400599816097076262424865867680957927823749646379318466788 +8207485786494651396331129651303688013258032982874538569357681541288542572016965208057337584986821728683984473782485195031290559 +8109594698571552972881573904540885927360173123269475341745160895931951290769944326526035760450300948152494775976334153020903866 +1739268973385061826289687149889229189956753685868472054090051279828873221786295847959246538258862203254463138510475016160891729 +6472588945675080220223551738194925153786626106262423384260557370130433634861152559973124770729011313810073811203315002214957767 +6317904094534761856637133735479679945651270156668746266850573869850630241143818143990553888552953747865302101114447970138765661 +6050111904910844217007312990287283030132792651432799525258590083326102644255354794948740529248987597130140253327270495396985608 +7218279207220544832644649289313013019030346375149234023654949206605352146548635130815666273562950986058561700625454917451118233 +4591089295985815819108775507988478501559092837715809067331448086286924500664248339776158516774620654659269297333948779421651033 +0402389695369287676425439165311946884646996933164719659142031175373351707836712648952147263228223229642458870791042562919539663 +1663280408606348818705021370639699925355411336760403455952690546859354756234316710656534265824067538349235703012760994218438928 +7331947252166763395452202287514396143003916184577374680861816449247026988630652041781591100537950521820855164931956884346869156 +0167992341341885278124354492799177573677966259864026035120911925009272011711402759093945175436356249669782240856162554344304896 +4307297970448548637044676901410933338328417297377294558704310254387943692431491233869228400552419559003867252598836554809147959 +0400912691036342469718967629054695345164844205186330491298158594966799688436822836744333087245877645780438275466738783338029984 +1522719795609161172147875781283157496792619720043728981895906145341666050986634594495047649741152246939806507306609930123284981 +2105358148148398274033567712304765375367909977448660123752729616799410712586037852322616433905738649313626209308187391551045434 +8184050603718152729487919051152176341217780682835043432591401578282222377677030156609712538101741280977544353648960619498088312 +9780539921497865045552480951265192490326869876857256607893887668594821322177012473373977135212994227092237257692716960309499930 +8387530951877561556746885813088050059098237284649866946622347108029066952894002584214015925808803683837814966224683534722188788 +0980315065961365338422640630321175434205379462185984915822930473041804521924781482514510231643402826027992447752984590090815631 +8885215984427616592965846360943654237762984211426187791836287528989189066470173789608783423601639354776027707428728361921725733 +1713545761488733797112097100438352878478186093850974631628293903780866218943733017611561596620119895299713346283509695880279900 +7056694123692100328413045552536384469910897094450012862955826233283495356986105417266885152765210669772149818027501922143072697 +5044288331991180937483810266252299436099022518584143328384527105505463981298174129945339074070183868941494344473296478243433481 +4945558387028214187495664309036767910507538072289226775563326183770144119596728765797237537898375774364456608274929793323186859 +2322622760682958787774419794236441627708513520567224931868094563846705540632720743482044172976408519449985880794628778392723979 +7704734167762682724779119792543440260650654515136102295412021984201525761889238911775278953009856036983484297176809989345351898 +7538438504892134535588424111693601136717057553968849287415515571796218046474942306436018172239215803280193264125771352664266396 +7105221455394922182238669063185805229847494225913363125711170315190206996126469146603174629254473614090501455991736786538113046 +8157168667432314271904818365481089200297683424381896431649087725330131885687876217074390326660020019876852812089717929283102567 +5370154297245853419804188360942032243192631168128880436140877705786486797146832449934263947276552080089455965696884234934971523 +8611893045023888017310198960086212752413461499017699656702018998940525757023063840379418108573223175879013713671744193748767015 +1169024393509843272061563781622978325016779395605303924999369153283788827391502842037817134058668089767959752756676201396896441 +7712715405049463369575317556974267677998279848759269048895603544096481338768211601218741510541248300317436674252540127015114391 +4834430831243295519098671812863396000922057166207709070811235493720931115916983299692520931163380069504267039634742271991229413 +9287385557045725218161176454965763264083042136444478713449129334549410452033352200798030615759042237027156709457224173930955024 +9440754327156472541302944326043902553561785824204549770748860453197398189036670911169331773914895138408417009555757899645501252 +5832453847611197719596117303718224745254716796559500451686231076822545459079358621425609803998655831572289152307921157534370594 +6716617297863540993457906819123068181321563782640587142663699479729095230755403564453694626863314955875318305864016967285927449 +6751752585208130882471772146338918033811645841373896388668212294917320582400645793013617832138799866544853495551795042553770974 +7292062075462511235154219336433250111030971448383048960076039173673865773457243289124490997622252180509835724029497464904686356 +1703189532838988444479300672213603786659157307392869303094005510273480520540799882625565441527626460538592793551679614793917597 +5131469474921792991086626550504407821816558283404370799177646663722782715758886649389166252871185413421418269585717891891693033 +7009640276492618793739187876608008054508113875022453329081433650929497189681068056166559133270910556458288747450052550799593778 +3775026817449250262071158797005661647517869545017156045641434181332078515755353689003930497030264487465467676524742489128433074 +9510101337115987091584562788637829217924129844325702143952753831115106454935897717213086048464400730491846668741988349250815009 +0460642398157894924642248578767274436367563791950971068099635643161955689433389352445513881354534070645863359289997626236597102 +7422701275758347504612869999447090197900987293675125648746188541872170309069628366658720207642452150511782301406643296921604925 +5722148742542704368310731836532013521833785510008023772247459847917061172721772791747731176316362682231389997934194900072780568 +6833294401022288062004523032063979096666714062815023108654665205678873446818506366372748896978903704125253114795333767006805633 +0409503844899480726742485372066289542014116663615248040053134473634302073503009858583019797024883588714394669118201212140331049 +0801167094699960082079962007695468661043658656967418331551197267388919434693010620339366679098731235659707521841235334772751876 +6325143950028159251006278830542343903327924750282184108026273734472327418765244935721260982532015689685569244807990908472766627 +7594072557268049752783928609937336033485545218183637235406635759394992632389014311377829965838907592318760198667367240484412343 +5995370105199099659966025879437447340499996959986818273399469349454086733538293008250823125200112169460372394345464200255398128 +0278006855752348475839798168839058164956107248521078548181749550904031782397298429598863198195066959241173235250539525076363890 +2434406818995447305231609382502170967538696884871023912846712870860599713914006479152902091530717543673744437190057705732209450 +2049303158709878586595288112356673888760379702189055053773161541139894881284696826212357934526729889089168985110486922861784281 +1480496657855804936389615128385812000095228481966854328817796490269872553734151219601055960998715934877799282526137718284428232 +8638224569717329657108787802886111345753795000064618991042853502960039658407299390014418474102137277340588865538873397922966731 +2915504391345921079597753766165614369426311302871553494239227907927104755401277811135239511652090480469130728548314080193845714 +4662543498990058418814498281187250236958894998429926084397929019382124044722273292092351973221932689481754974738766719140738353 +4190409245555651289372817877351684664080842342410487693494611165285252514427042631380484145603968868053004939443493512729765220 +0119636369629430093021488300230179969441757666478935315483114992540843688913867607317956666306088259979412209337113112892142162 +0522344915049743379719646407210766405374162220565183535557053495876729008927029380341012939110046953015244548503515867668213581 +5545277258914067549098676802369675716209629211229823278440028400844384130766676038181293025311537464968008032689589725571813893 +7241917831784954432887951874113610869422570389407469506150472467208362227227524708480496950010505902724279188045395824562149045 +4536217193131427845613009167815485956442321803951137596941709381463070724725318338865720453796245623311051568325407640234609643 +4950000863730017668562367944404823933355943718044346155630933481082791520987136962663882167172001786426520967022022083855046918 +9948462347102121864710644896101448735320408682918327475914042821856145185407258575043429392954988610306816855047216892742882375 +7607463192368539597117886391111127498537601742711656094819081284338644518509840706151881140781291037342960935079170415736099697 +8170547034180787962131392617751093115453333934584190242119875787080041916215989194964046994229463391170868877473670939015394600 +4967067863801616048208692428984726982425267929231942089387972467090049190181520316912402532109719978404281158192349591335918000 +0182827511598856652325488782019351042712700704721955971553105284410000877214479216605042256842199795133210940783113951447780445 +2845448586343352856456977029480170139895515001400772533947791028664521178270989590999530566461508232129548565121491303912378773 +3186958087525040737796735368369363073980409705399259078085185801788329795473492605756831924612159181775569516279264645527914405 +3736227582287369825938099577236266512057139246043733096797986415476742434871074557767615701953628932757311718425644125724233706 +7173052386737821682078985950554267597062731307233678638341260916391599502956775154905191238402444341035490900454688563799626952 +5147641961470238255748645042116320141678230506441721302812017771332190399762353180889095100320685446894708468194573746305094928 +3469096230652662675602711465325424276546888133056279141284661309882996606616677713056958440903862720614419241163546636652676601 +4000176045790898594283018168562878609386243501149259496688481941480198035420169390431588952838073985685288362095448469635565752 +9472291341301030094187025146867336563674988987757598438413852630025766068250538948297399599973328446303590662904124689712678759 +5558918453683884045568116648619640982031099812596210893142179251292478971547413993326289821580230193948481450670535256195029562 +4928975689440726890177460501590676962108712941853309133182477027909307432937071388936253714931106477057738446899643424898539007 +8940244387920877438974129853648880036120882337129881652544245505645048735775452951748876363983239868801033134946650378020657016 +3701058033103724656733182793045707161803086804756890720227008032425755334349001190669627279911430274518624636020186433616060892 +5795048614649402340472644254299779685615196268626556186825992241948964494636129874107575230767378691185971658847979564205979820 +1236133846548113964258787812605512858795064801453710507748252434757524711761598464790831987938492943750974650361247007836117825 +8985069765244610015917016588371467517920803422724008738355002305222843175513547415525425623310272808519127269635589538855868669 +4866637881580384758530553032050127301271010383021829168699459836736308865544704254424297016793922754172114529453114659796809124 +3421197137282279783180092595776656118340512425979400729754872785822827951114365899638513626864234673281733350488813180030409671 +6116540261589173059806435182727298140303534088364505375974117730218290182662177594123794279119735348291936701406861588485281875 +0383388028538445842643371054045990749985217056001543711456417105050188186522673179186319252532355654228417953034579705914850138 +3589398737652268493665223622209314271095213796466114005222254738357264490629049957974234088293478306962011737255628545244120278 +9242006644961491221804556688452952473719954034253935879343918188942010496090705258446852742963029458182305909452754390287793730 +8783068031607497439341603800503578168008017096526972920750091440007773022446775696934539156855822636256531074692478856955931965 +8800823547223548709312492609422799167744315137636030763502283934241170303284109496261288800964950861563894408561284405190045969 +0236871653896771373931349637525093015261721424149322538581449425146949187109336492808412768678305259961228548229258526615055933 +6808296006076261647807027264924130647898638203490518417736498838185026931804266322409312178542687977048973852811639981510309567 +6711783212313531503601350937924384067635440627869411288664439772524317458074685185346606914709019186196743715338064452022403271 +1329461726620692353133812940387503225279782302791789101276394351197294472993751698133198738485236134603208512597461992464092534 +7712409812790062450698418919781788475967301395142101015147156004348821394973951402124816517835984222889123201533407163528809483 +8878866193884252627688867520926767781102452754391877549372235240772045942305940402248699505448319900090404392151869967674502128 +2483008370299951549301955588790277512316664972307031593695383033586616221934522732614016251454351893271188672805879216575868904 +8930668992950485150931537517355790279447532793020359504907584544928499659083716918240980733762266979363328381222043500384239731 +5449783313063957701679502215046202754515996947849788011011244404372242125949053935941173427778943061308870747330558976279745268 +0087442866677315281344307628368254568290685263159851300491709072441620656219856549470063530059686910247545618397733080267966459 +4275923776467144475919672663054261638444876359385278313589184049205522647886045588220547740175271213225571694335593813888699266 +5631076970562565479284442184748121529273149320713280238168313146627141356684124916604998541431548094125068927278902251637629771 +4049873854233479495273404123122985718709224211298472591495302249456292201698069758048348513785824572699432884088465158829077277 +6601450724720706904135791943277181797939378581152182984849501639363583354753791712537234399122836156611327530479415218232099189 +5823047280117680731777259023254262177630139266484401163220777646941723211983561493087099972956890435877152661438838138706007958 +3871525096685339470645850417153350859644241363604522957863652066029739101996436761253594694922069548250943637910124220796682359 +3789021225120290908842849945791566951268040039302726617891236371450848884685584694857899475642048623621033883942963333740640913 +3413392907322042998743788130668642581010260731658079311951196515199970920919298587850590756579979416052745401954692899254629962 +1834576186290812892686277778078188491493029363265052305832102758282145333131211542287672262899869561233857209736661910966320185 +8640618069436548861856298762841794920426604496493168277782652390652529026370866518863711574378563335650089395301480857411365309 +1062855694706861343683613721473054277513151272875708166067829305351279095564881057460121748427296483260938003342506900294399166 +6708351271129793412643921214770696882147428525507519544681246954889801321262715754608536636546303081680171501901796848282715773 +8170065360748835664225539865399039511547738151434626699419911223518847738407528346693417347838281049824633463358386526323379557 +9773283803239608948889044323843524447778250704201869489766199362127854167469574262448141201863158740882252497005327174568985690 +9629170341829493159540502281159419978757497174339132063688621315745991750554750919122418499757801567167615195540686775308036772 +6813737486648360157997154949184994770910737848857708594705159108226498818076724620595499835896899658135344576077658527954169440 +4916163367448740109143119472115171909713176429892100499193749017654102572161123098799425438195950481418848253249246794538567213 +1943648585194380435794184207380836831419456414164714608475465257187830071751668999365832434059345210749956754565801799757863175 +3687162724713568040156043932976576841434569787909011626375244300886516755159434297033774573216110685196819615175124148320455479 +3344419757198679638351706919063941845054040199124828713172593196435249592482470220242606177271518431150127043364815791934444495 +1400008065520008098162065199768125759577818782918099045365313282741980846275621548204708560469294053250492025107214712766610441 +4165080021198705915210786508550601653226551217888163799551833017968067846988341804528031533804959471453060256667445828562593413 +0316539921143167379344751397929218943090424282667061717675506492829683232765044382831375540458280475310930354270466628024378573 +4617204749069035253813713597661824629929473681227979210717594546338035842018470292109616982725985026372103084242143325201934501 +7628437040946835252535863570081482725836702391432238382009021257963849956941583278276159017021059243802411805222128391491555532 +7118276592277047948538579672067937116293934455417400255981648387252807293008272125197089405857447395662820745517911550198420495 +4817641036360381588057889324736649206689772293316553388319983835732003282890192751493367420760022776216834345866178180918425947 +4046762929816361002114402224273088310438847474234192226897299571581711857975564268312621600137920668387355607049886008638057179 +9349209580226030857313156556379575691164723527464611358492523658913876021766102463374095835678962118324213355237312840525047153 +4458386832215216090846608267034928117933000209670619365236847239547649406543272314067052926849866069213065858041288568227157782 +0323889725674406704675299432615839196837602006707700866051572397447613281818928460518366859474985370527579091269025122957262918 +8693066533952054034279975214015256982660363796184125831239795876267900616583623647519579472331003311428117914837252346650454796 +6978896914173575690746678387000060698232734806725458177123404206650082946025871125219582890983005045333389163168584620102734094 +0208343185195645077429621338459120990032615146936965171481121547620265509203707449471143882951101626839387108073423291107985197 +3806694565547588080124701297594558579857621291153844744163973871845300036486064506978169189349641947450555475680771354116721127 +7571370673293744335868791633492268450482890329783462471218711903016708619390949407758233183647172103050542587791975330523548939 +6895867004702083992837053115350359081392183348123057432804437516085126480426796559419343457573809103554769814374422319272987621 +0493741336702343083547043049060106406621621423866156744878463280624931631638757753907480473151395705318815364246326098575320029 +6546610394497897246218305493409122075422636049647756434404729742804710870898441487104017723846320578576625101570540009682146545 +1011863982645118776769923031005075048502898177517353081584252680645039932448249490068383719149012492191034530578466311073324350 +0940022141946663679361628444591959010381878575634668757912852707910261260983582304441366347012174417975464831931140858093450475 +0648068206853955526969735294213923796512233785864528730934169006767896899020079186828212231303729771906306615698951701782896040 +7705404267837637281673906483600597132329707232579985805240515664780686649377686946124141035632716982723408330472426681691722063 +5719449931034079305356439666263973565128713593058013796099786122163872295053911196817120195135137786906005302955333296169099953 +1565007759742421563846584208111412785562612780177121771600395311757381072903942514964351796547320888609281738762831301074727155 +4840914538717807330571774867536594427640229999158918266573255879619306849868398528582717693006558964074770743956975947230605506 +8434170706163547151705112191666162854461320116071520338539133193324696905732424568076770881016026396766250345270611437134977029 +7078829392513262023910437193357139649522860536333445198136202621793588265321546711781743395796073320028953300954811115494631443 +0358109781392845465184944557313417160881340633391419971812545527651450314512749202620144080637491419201752326439158460989691315 +6502021443239860929638929077452949976769441697009577432157105021271893283919677666379901876413440307841861569738215346309784385 +3775582600443257869423701993595504734055924868316007937741523410145241997102735147790121190918364977637486443176097526249923371 +4637116062887697972788560169232687166734847960365006931173635513798426534363094553967229788862861423059133919581108100325100963 +7530945856385218994386045042760138610248296085030904585153248074916348965900743636913197206787899808540051760130445456596005975 +0082651589864980975976353455996961471089119847766125021188045871260940417581575204228376995886410553255809049908515562710519369 +1760176651046607404619158827695196639809040360410321347276093208667279377323033464221881041272072298115933348047136279306992189 +4449900653089574942697877619922022124081013931968320543380925350254859461377805018823478776678186127822712476112142520129506611 +5360537273314488942383696459647239094488670283840872805432523968819547360255856276619301822761467290415157270983258323381158842 +4069470180647648022724489031315390330623675246416531426538288357194889582860078070141817901349890459635960185352818352346101741 +7844251807909360860529020528005655952345376045413861757127236316061244993838373872852044455049706576181912407467785016013363013 +9052782242469623627138305243693885375972159761715793565717369599576257075617300500868041008633881912456191819467065232604757474 +6257367548773919654525538912712606831341580701967173140576682703453195979358288347264315572356542410824096892368844905848969800 +0225531033632269859821449136659749178099595609866207288912288428688139123199517309089785003185562015618879502978274246919977195 +3412420395378666741269488926730591786905587902546167052955793049551854045851796515140844301739953615780488764468845360750173095 +5314569125098157310473890637519564038989130593324658307078574903372187794019987998391252363359372204089006276213106238012887639 +6663953749875926737127213800088216077686383881272422931892106536521390323291590624983492836817664288184016273391511477345251544 +8638673096407867791116469531963581105938614830552680798696429006418115851082700139624263097469936097502648484256024821946642846 +3261638416472717635495620507090713763983290274773473746283045337514051847664087913092670813304318998742601034170158553214610079 +8880759198069479182867513797991706527425937623927208476182273090404608783390855357409578977746370113730628533384722500327769010 +7964841863629197861004282115308229708980836184692237168958174288698996726561927516939282475998327300490824455643829853959878546 +4078550144875137620442196894251820235783690433882002285566789090860837373159507997956136455505729693351610325429624900264497493 +6050282912476085926950954499140111563620447106769829012752567207785593165136710295523078591701424407957863045561973215300362256 +7166661176552788865443610475451627720514979209097163741564119254519710530732032781304320231265321064373366029789454524964576034 +5731838949398822830420671258681703806386818639376155723888774861742977966282701086517327314375297715355669245126118723701881276 +1224558805186365369295742263213010659311742471260411491616914473284160243356280450620150570408750609913288411042993040473195864 +3965363040687291139829734658431286318462224708522925124414942719604612215357576250193871596416258769483377169469309016314582237 +6893425774786606424591335564336597467021697313261577560777519605605111465025332099784940659090512446671447989183060023172745349 +4463023117754025492704298169088346276715292811942385607520313996627188228077675005968319409041724080200894220147117291796736469 +2838367610734769148543248796176416656292040941138404572575878998139182976355595776291061040689581132913463002164503763638521768 +3585007037968899533646050958305472231296473689572926896126127396786068936357337467731293075452760614111507010520769025993887156 +3025256134722351463444782906552873128386686368275305328178049328442606382059696387241124580313700815579068965097895955058791706 +4888785355196252887855666442682481926655016587897905441501499624695729356509071382797002137717002280304447105900656692938689937 +7373767636755443505506466024295128010399690845976961083809222125377563226037530095320414396885247587842781981216438439176626761 +1467040097296217610154727986310524824930119662672041177376733844872215017687323322767179584613934704188382812120901100084067248 +0055261010703792927114582278798343530536332871891286962151403775103518820096790537756954678439988840466572934380472009105374277 +8745186553258777370952969850546252235196298235316976663904366268611878480055244838332857644576178557847556193922108941720480963 +7310291886872239579954842039199219595404202222951474277713284211951970030467647252785199038918806323250107496561396165925992981 +0269739665100112200470147709926216131752562563116766321854129961314938822821859829783043904077776487245854331065090150597583869 +6022034598022521394602277250974332652859372516801989113143038045232940829875622476147452690929605127893923207935810728978176813 +8336719184987758081413476270877983896287998313373933528191759149653154025750363065606417948489184358633308380877845477894994394 +2211461131853873156810302948765499385999974459433061226146423964769287619911252503115962272972255899290410107199334224556606196 +1402532784763926313823670409002705225482185592078872442217786835593632982577282903273472588503956076522785281354947952227775013 +4429564170373891691353053299293858952840011672714332817264920331107567378250030393312486800497701640449816183190591793478209061 +6696515680454660415306312055092012435204898424655900246268144415272389485588150485707845045690614308427142660270754614963190500 +2858971388307084324374434247865554208042477256400063151868591675576690659860039137955754547923321131572573666015058716528677560 +8082431519645786853787074095747644469767343409908775777981556502295146873300678603062293673505033125364497124824045616025095406 +7796338540952910648733467788030865929612271961440794230689497348772551503718356824778119200166125356293580937006896521596909494 +6623286777262305838866280436574493459341503722464731387299068764367690003490556552997864648359791608273875942027789979081747901 +2820762823681177751588883983434913077317012385982082727245344446265775140411273692313089468167013918322825570599659499017461253 +8261747834117161824733335000918969440857899686470868022119901936951959497914248555296472517607486195543086996762943719289544222 +9801799673583374329577712601117604429044210658931033348348701101269055569381455391166077387776701104476767458640618577366620088 +4812671524372167827325442166901095131227009588677251478248615586184246419258110774327904940522040756539440861199193515869861850 +8664636186261176234317788607818904273283245654628256017166703040583397980518196864696741148650137946657712844496351414683354360 +9233785042626900594870398424394786589093845861107111076865742457098507103631392813148996022790966610376381301013746723765671101 +5599585345939628248728616861490756773613146232759270658252490655694524473642364799716984131253156192355158951050342077829511934 +1018115413367504225955575347257464735373797464835594482025360169817251469418319729479090655145106287600720398462808509722342523 +6277929810316831402715379663360019513470321307271888286707156176722601290897267704973159272389968049881611196311224142002818283 +3935507653397356468132051961271044279593473480413318195705016813924079917284710536189206698836513833423232062305170527285530270 +5930541214728728710514258849354991541204981028873011695299038161284051747700208580498162480391834623057628739847088944321070540 +2131178112490366896971016249779880442716420462976316622931349965957507940779710818286842266030393060843328131735676772739856028 +5339433698747921036431228649318038784378241814872129036313002907877189828316605458939009159662654429547906907634851960924867766 +2725144295031618884966653121214453016640389429569724950019640175004023672074204183536974715285043574870846234625695507751624691 +1101023040899658835128880208200979236233784480432079242348382094125974376887755928733596001730428491870299057160651502141598807 +7529805201863368482438225714843876867695794742159176187308623091428831090441762886002331398279108448588579520003248961018841987 +8520639531359520492110038481387012497220545183327686786319255787574425654228049283708164761318908288816634942299623177967850311 +6808287068903794688768313922352685578188040716704660250946776133077382541811421559714303292759615754217647905753791800896114429 +5661937959933192849164004778980526700005624355990312125795014118394734147334017327033918908976463421944496702296931260093163478 +6866390898079223822127023182717821447021210719486206672090868302561371850042810557962915762631762160188811564644765871940463260 +1983784957124576912722518060888269293020694751991232003856484645400501387457614453894654158242083061986108255912461168024044362 +5023757063288290577909452112558045920010016969071712896564748738667312129264760935144195712725562634643903560619975222875598092 +0140266427651227095088902077296483470532188392053269670883135265668245606745264278532552540957167938303029227319042084398428852 +7212818706985477890259174108784143029357600406814727037979412733984989889987109358055090912357026404921211609781959966663957665 +3885309358663398776897062600124335478996053317484403001725157655044282776168453835403175508740651996708988152698983678588345737 +3549561496006579864153172902480324325137781423364931522958523431287424861594425297230970169266435951937028485126756644018421959 +2100522734881499893473520957404272588614489603694208305341205355969403603944823313607027770883562580199458807047182736184431355 +3558566826776678787162145637003470985297896846344237649664211727912542844807936273529611661291547660198761650362176216275159276 +1697273051970401823436818435327916444246809126780823246257873146180484087471375537086616586114897794676638646530157453530809297 +1877141731822379123458355749374949833686834219493808460922026220049948042459289931477172440353720942293259692771867444391010663 +0596114677690135744062023310258035439858771664427542203238516588128327906442218908975480077474481540939986337760369812161668739 +7140350695399428116941903267710113882542083290951701275369344655927910523637037185426739245384522097331905044898640671070017500 +7453482207139204385313210256702640687353020750058344407280878423180101848431141829884177527114462939147014281506261604724365378 +5747051408867537658614820169384534962462911220360124367460201651929905485919878430356918185750947336103425001412723806012420037 +7672418158298677361496394053891368709542765466425399242228084774006250993472472890952527381058062695609973430135097018848485375 +2721910797057123953879416244095325096151158592383520642643416351496904866623597785985406941433201519864328401275343395705908857 +0642983563496035432630122777955167888076028823131047975325063367604577001605053104073667764503079932854743114362110563627584697 +1737450520589955785122525243234607141022634008326290553045379074437558301788691892914015378729774742647378096844792864200227145 +8049477947243449894011256046326611511617306926576503154750985615770186729781372043096871431456514521234719945343529143211616132 +4060930164560164022219102032312173710730634459253495956116881900466752536260684001258460015562183214690371015163609148581576715 +4339672054138260157605312889148304084301210593319661500758836115820325521753635857005220500312010112221791048318710925629489360 +9098118884517110952549620567550824645268745886275953112452963794466205654973301959016969502644339312155829940467386423970146851 +2030422710629305679756784568188030405053813130034967510951876362126306529129246017456183221963883892572064857246348841309902207 +4014325197777088687588337561652558142332091500654763031260105487352116429628561906919380099545877339112314250562226830515084041 +3366554959465306038343578751837494403196208574534790117389855546409717054044483042532146409499847066640874035730246190276785027 +7784874865116514506998867941821004659533959527747303526874272093224628939120659783314979799979608075209344316698881835617720838 +6440603777916554902557821762272317634207749152011454338456295029524399535728380230269435936275288518444494136138485427687743672 +3697767429372731488197371169546304182440802429138205900920907076873491237616407487249087243231746614255961699354564621444166710 +1737116215657118949418096366707426664676852506304031637797388892083081543972250240515279155087924413491083737598642466601038186 +5943501997584026886556269657780895221836455524956899348998644491412008841758015117023754182242908647650511196372757530427844200 +7126098691386230123347928604422159107062602585844839531380975985325229120484080904014468470157441597070403077589128617082084491 +0277414078111008949169676840880976444791578133697866086476338429943906280786246925904505101785927908752510981120731146336711732 +5011921703942318242688882518650473013576715256957336272842712546671709676486379489552317943731327342059730625728957322661925103 +0879272542563908841343149899911945430473689164183260890378649854294324088836226655479509126988786763035689227450447066454969580 +3635797692910230406460094517526461454920959082159212413377990917407487877377494660930246389887432321322711771671714018210018244 +5496909568572645176488382324921312621177838539677336553279411765862833091060541060811106495286938550448561214644311877956532326 +1372848285800862888898722477263959718622329488994306400315469392140836614515990231729283982778367781183582185203884353672168533 +6002585963544294141850925971586001726265376264686412361898087738304610666926697454083310088708743790359728974336913706461189057 +9544994601782264143680659072037087516325897282651078850550946582804440990613134903404213391711941451230258293606634693846961977 +4294296739368718314807261871407488388139897913896057193551518458028526780686105950168593279489650803151515173510050942218207014 +4440222769538547178501749374936219442687195706297985586916321673062155148432380676822216859833618467359812508595764374585042802 +7090291929148830504311787542972555491835675686001439326827409601590115100758667523079283454747571560861565934370463671494879471 +6306207613636744929606340131323412825985507087453258431275338612289333774848983021189785244607159387364635876091430264215902551 +6978625947059991073203270373760209952614062079324674488242868605448441131389947105750784088354616643566884781392969415722350013 +6876895254868693830649346298281314621651851243964761424180528331393082774872122268180557991527876049313013835528942921722633863 +7994555257251266617368145187318488777925218911343096210380569645729516845387191015748935661281511118325824054860678678241487526 +0561712487759666786219959935849355827031795611802441457990195006279656115907452353501575006228849649556626967066612451740817401 +3065700444272921097020103095032561392189274167240364636985217260309148382438375007728649942200430100798750588348282135626863041 +6418290958339960432209597429802884987767056126860829761197583897408153172039938420304452769552704862796110442336483929988890230 +8411149220304441269312528476234638082926201849344380193272700895522547783655344727512614476846755120115181365390739989354909216 +0361827006206843386504436443633849307138803846456656207910374387223474485947474439419270873842802147250905121223284959187002960 +4561901942869500550331725452244098638243866570298342595884556094097612128702524209824571526031229091793929079092201659156014426 +2569784663225002575851412395224946857021846888745089626571792624047307039913201999491767773809090290291368272189546699726924662 +6874345054057523806453164208085928558404937408086499251394158950058605789150302667994062909598865360566447853006716951800116507 +1806892347177795837940597487685027448543886249836281858785204813002880073746279096873385938335081449362375954555853598273582800 +7280197984136582079744511307894765629896405494228558457447110951049222331477610433641974978714281803792968381573777298819652746 +1797088044067027854938520030542484156979879022746379761147066454374087806486473993175758842146367406488679492927645706548068169 +7653106688789014002295870215309142445613718219291306854675259348422471093504331308963443794325054200760084750801107789128152322 +9003923022817082834921721240788410357242843286036977355497738812382673303415071169601837346379966066053060156517838544048108090 +8927903196351477482460887787174517966173931425526452434853205934174795153124436599267973662874006233354646357728472499860554504 +3649137050137288600334652169002894564926317935052375328874723351133720379648102007752831671041946276927861278960706248758820299 +7881676548153486303936365019720006418330263108526672450012829414081940012783530554845066547004930367871816825750941800388750747 +5617499751970255706526710234532717339068075364068621300856559435412791685014748892438638038434662792832126729610856601828058978 +3609505162338640897172019393757051075698357002384746777463891780931828353562474692966266497051217520058712600221078906316845512 +4218942164849049619324701770389240864171286359070468568203895572380950389773235867347931266228357895243861158024586703415416725 +3286877811857113758575603255834974713137243819997694752214133530728139390482439967896436662426005413536915227299430206070281748 +2197090522718698504657769582076344098155286933671656794750871593655253531647336100969432683359373204499872727545324036411875127 +9266329520011965890398156813860326403682885444460637962832683571266360623432333340007962893574859386016733773045469648300215724 +9360218780816808197711206028145267393233058010562551036732947883280144516446467977991400621686114194941533328336858706301853920 +7975466727102526807928730597288066888928449183473417942716701022737828267407136547666947638756923213101355014521544864295487342 +5529807372773853881156331115996145600478892095295063408262502214227819442322854320893250029815358897498644974831455098851752144 +5217677466571168103885153319146030362086428120742099054171064034691470505077333070394007756131235288630142015715989289401952345 +3238751872112411965525172279795754140090657968922574492232135102357083597107656178253770512768121887161690539858563177051498031 +0621143235328817027584926551312004300826256116914067938452329926333908173348351478521275486940271058902772821984682443421901374 +8675029994765542582556780936437230200725353322681947577235834861538086815882331325782379515826004655685444830286018743065534290 +9009226484124905023296329465126310425464799999137557622919308471018246198151834136297559364600044830857980861753679005100073906 +4026810600660421971978121050945152770853655639964897333811108395111596792046176724058242259398267547036042717629901672105588823 +1701174760060091817941457573061071686086097383906657079983926029247083644956175901708832562604741667581501354921250883246878076 +9360121868675405580012858168094642670756925814930958722345840360155035939830719501362830202256115680429221284715437831407183230 +7627384360887381611002122676157337577850291681707758128451482725333215128574377639863228270429176470896652784523071172226399208 +5924717495388805188609979501739528551778957144375491268555383034743525334962407264117945701829683575948250702090310348063964292 +7285038571762987494406954123411340680613165769286304746181220668099890216085869650378284432589286853709759806525223094629118994 +9108371459503424585162428035918267612861106220506440110173672498423384456764491877643881942033565719022603374698498258614890281 +2119544835856779238923121210216240209691871364625727861760840643200609817224777039142791222829730821468075117365482012776292615 +0287625006985324113383256843550124431381565006783532108722669073077586017057966762263289920449445173729525567088605286164318499 +3802234646726929017573814267132203009806257343348973659561216577528599490870752429375293883854127262090614163544620710283467000 +7574267833171812189998339587827378487203820258567218738257171370030553714438406356225807045913475052445364294909346459733645424 +7057209974818106332836072380332490777736818456915022774135091028181738711236811811377666386715288877847647340595585098875240481 +9197215476954479756497624010576859326493604058234153493216522641384431342823687635555610194191608220091909666730522341780147856 +1119537820044774998504706322100346395055393450296026673688711639572331613882591716507869222690391102545737433635859812821586270 +2028121903848368987212538442391618102128444213522329584551212886903253244685970973210284229639689302832127513289597893485709871 +1139101840913818162477340055623563542434178020287614160741421605723686784042526943742745792272929328869556547712709877327596063 +9107408768006237961652791681788083610784715577214377191666576416371990600318918309907846332307177843177765073709224406749447169 +6733725726008450978073741092474719053157935773438091672305982444490248804288080650618594239161482780330596854243463318510550141 +4618707611170337857871421415147893285747404164961249246066409955331826510336908541776726066361511390478520619795261534651528646 +0029871423726184455790317017373341560382122160829511677232857909382425970016570928985722750518023888742942482674353399294113016 +5851677764630543407742209257890179166834894877003624901201481696534056706522332095998210032002750686323425734736641298781343895 +9906317458124808949504199178481097664951999312281141056844360268819241235749855328929404804574246937697891911753442746410513511 +0301178939436270473741478421267690206733306236983147974561800903978683929049444127508643906957999471564453966363638317098479091 +5695590670680910148994003236516725327853434675592633044599651978504490768012904279502213144296250305384458215615813612885360699 +1739488113025246606429350291878905105006718514335177675021282668693092738324431010591942084632165356255063331639570681777622822 +0452464599884896996261376953519588463160916217402746423924422154683861260191652961898971705302933158251417160490178433853439776 +4496351983750730650624656965901956261109512227868877661440793378743454026849129576683511149685514643006637302234104714172349199 +0650873766138283576167959756442091757660353703079228843249388294567585567331488037468607523624545996431350612623181744202876623 +9217884841612782253645748182653936757610046223059439143748083525756745987386187135434504743166765496977544377259895573270803669 +5233343431863155649799928568730055354198316775389815272387174921789883067246402571006054994197016258947251698092729387460016002 +6222471188835586754619063338314127078684770466144889169048987130424673328124774962008878054843463980934491177848530073946295466 +6092983987521059016788973113331515120050882840886284842856803861355854867424082016223127983920367739842939341557083529339357539 +9152847423135593002663232861455632334026991039822394086654099188131348343461957860123922177650787602787823148319677335222779269 +0688837540315420699495154888659745629289164825587079409113672585691726485783954257060693018031835646491154074592682796993759642 +1560331745546205131052821650865965767504353088368099838110467887791087910699782310016063819056506494246788726522788847044002972 +9843063757395851013539217226262322774482770252168515327930783086524549572291277963238718919614771432829552926078930464389873382 +5165503350814374626462147517304790280393184056104578516266158383058163824599464967401146483647756651293496532192439439145458877 +3334882919381143165661074051363092147447735703148534066554968559573383084492917940343234309775642206678952812922080261015609561 +4150090892422827778476783958648862075260361082863595155851728025658354185826522324370827070485157038772970922137249416500335139 +2270729610239360993359408455484013545432335012129825782822705159183112780581412024925958595707332590452541348227573704213342055 +8812698543571227460191878467610741003808872140196791243938096775905061169632652370417102797369863591037024610630210903737953654 +4650850571724314687005845773201562588529522607481288290365062000530992710204626283641702861862037299641210309359904939817933700 +1541143632255817063903213640253364698002930217355939377060114361273809860644552941254027110592936933650281526566113066201345129 +4904443218069061051090074486584977672087200091073688866301081198367118241651115030647481869824608967401802054144740280994709964 +6714819600988123781424031251846423957142678675252652218273564424036686308763026328451858255344930462603371376203101104448466961 +9509079835837547622180222314290215064732353288206452347849664411672496172109171750484558066698591320017336090478613367662057930 +4411722606999341112288605914814611482715323396030160357274723566603030739104369997016486251468461541345322418772128759607222696 +2948183785699572384084816055776039138878581148811752092336317365102276231176457487558011884744609026187961229782054619496985034 +6425095624545385534119498681567553265889108179726785664829530547971892685921754372850536788921833807429343926950424016846919855 +3908326069100657803588698889883675042505627231337237747168653314558267673583632726264839565017058848634863075879312565153073799 +0321367266050720934662774520376532661682212427124263069420126600109509081952403933716651001460064949865799766176759028464538738 +9741038058065551330442704073919666652946527397570055939551345839830820648016177629571594906346803356773604688620184511211636838 +5025402557710775296650014515224591309232698067998266520271098024189399721692010614452489061642157793190304607388980065831660161 +3865371756663396962706440110406172511579502225888432146828231479035598172810500698253040173626514931478221885791112009689705108 +2972286823839979161832311952971045738890113686974537517223372452946515286365656547601871718080007574659696313592727664057554794 +3671238913457833340723922452738592109309375009714705044237276003225833183922594035143870946960395880735557105535062577056078657 +5639824224660978396027300014663032753936090244783494976851962014859592152013834741021570506331633866559318037963618148063022083 +9565507482964988994686034476299289689477300610763650942914931588839058915298408270882723496065586551944274748829842524532900569 +2950174215698554483745941905548644387438215825109435969023495189379067472134512077000118600012988374494816155364721054534508820 +9518525904194943790633186346738672276318778362232864095452323156494968062204689793145701834333799109246633884847638921045253621 +6777489268860937675801194897856962853781999286279373198884776447439334574892992495036091872659389999472183722596570197112840487 +6940178173947556103657708676822035069381549630078448878860057242455764188710473335746627500154960475546070307022636234086868814 +9329686102999304427635999138246997518586345169303923543680994865014159368545564362656579126189568984209672180187596509411904289 +8400290546843033015441275084455799605870040808860666105081155326036596425585507607789010815662662324838353003534213828215099749 +6262731155067666934829202231823905666280056341904107277513506385716411353609764416145090669300953084810469016331307248958077392 +0982572667217714967858091868055542854621721843072461742877828288780619596753823464439165581485796328231801786913442847252641526 +4848655976411335420584575046674979122655105315200347515565901735274063028061587092460808297171218521824980966675211293241764354 +2938658446761945214970070404972488238286005305727629688975307477962234711112158327154676636619409638849544823620779316735831746 +9233723105727772276287999774922758635916582468201648663126732610340191585232715185516172590941978497752196971596838962424314214 +0715690795433694724565717518703035300373548926507910938888351542625042732566820561320308097626791527666632661894798286702091403 +4964320219506572216963601199129841376254539941692757336784083929771858734782219556315822673159169808692321828354292775097642159 +0335559611733921504268377474272853207366325430567729920137620204884173222566965027114331948190426982554186105784631841860371363 +8033508558384267940900957170710292773682470738089554833769290979485707252598489360936633550982291403202103393568639544279214517 +9104033203529399347159766145704434677301867369574628242547881760927798205454869510642633732888078982477565192220841911379788216 +7987660031763621257245807669247102344394837687835695594169011679441270384265326170270019240149525684618010449694008041756350062 +8151902440733357075969965129856431003770185569733670086864031515027203169324745948550070082363039150127359144961399154724592013 +3790327961179853157835883754649149214290489479319422066362773172577179735157530589191336297098156970651227770438081844185734307 +0727046673630137406560803707058042940068228879956058420026237531511228698088255979495039662974032046384364026969019919437568764 +4204578688546460366354226690592321231974552743860907666913839079477111780605991648504857092410645662298682793993079551732419876 +3756294405429570395559614676089322871628319405115443082409818445213875343993774082306018456892013141571878129826624772129903802 +1915879655542608031603048228973067523323417771894628549592585367737979812887089684899398996853836990904295238545782994463736003 +1862917064901509743356712523964887969381684280627429777002084722985720739317294453681605121270099743339673719320682461749376518 +5235629358000506017419756442938714321316730985284287276160530781176188912088650998156658168743552720877268116184356043543445755 +9443882656574676998601479646029201608533720364834332155558897088411735201080867283586900504743773546108777074700728937687647870 +1677493272605889337983961765432999426943110889282768474699096119469883217687878946342929523139815961885455260346054616901453447 +9942749715588817104901581207188857800673119328816832808571244811062495461360073125883981969697016073423707659358263862951164461 +7589849154636259255662987558451236625196817855393407896633655975124730683842916268495164065502043203437031707489979618651602416 +4877537573726160052953215845080536556910640218250727320033197474127755187683283945624270057808449225177808596236183681916214308 +6307674309759482316304083895463219733480617515657742063182272951154632706337489064696789783685482205360435876431996285794642684 +8643739931017169670588941204387320679922643218218094561807636190505893287798227669018730741719908408353128973453099243266062517 +9595878873331613803340153742041195527735780444717553915355354609332848505998881213100337678158719821578770031967558080136888152 +9826258075448305567391906666815695574763625260042694682673443923778013491658161518721464450500242508138338760627120693213553580 +5553946281220024617384962807582191448987025563460515052097885240434443584003120884005027893320006209592895012188176773812824397 +5351207991648894581591662853877103686069327469518295438143903554689747912914341445196157967066587607931175822601224101946313543 +4718020971952950496510335972370616351651238578779144226177776993578212091520634237018822715136461197024637982739115303152147863 +9502456279815158271330961578383078627663170821879138142148906444928946168505151984176109046790336568113096829851051335078064246 +6953240969636903319954311768057533966261755313567448194782711138760563765547507739230406557393058730942539222575666804143511119 +2069936646276438376676948110321617772893399556260252358509335079746651069969820285929369016951090713864814429938558082689945968 +1521371174145731236232976467624651809029170086344894245707746268123154057519935873219521049882236870962226595226944947282873198 +7819821085451992801895188229558460134173236266804505398682944828070031878272264470195089873005628963880175293100970223151449714 +5581893838568192200692899357118035029378571475716773858918510887575125236858310172913692992079179964473799145336381338174573764 +4769245956084317711076182507252518842194925936437279722455907758875435914157274173921143663890367325067229935879996805150485626 +3134946076248319258593699700301770098983689499295660906642311982752703674969335911328913636983282731424392564971128071529792186 +4653454953851844259383149899336223085116998954459207141602572224661191223836954928657358541085552778110770329785385935394034672 +1848527104180913477755119395683294511819189869818050532796513348646616780772576901429766012659310230873210004958312011164035296 +2958092156871189322732011639055425235756904126336215793291675441374713359747361020499385226744232860514052551561041374928704972 +2212834863497329670476581032210400927271310708293108228424935554688407632484416285274174812931270321948680850009808218617859705 +5759858970411718249018834966145654061840913712087092866138009251429104683711023685469060131756225674409696442152891897372343886 +9744590632172501257249804338242668855862458498369585009073080663432870189779358421204658237236159348444067179731646552147785093 +1487302286960634890703793800583916358993694233181958170067964772301899115902690629049952516027344747956086526751455372742400915 +8338876525824289493587802849949027999302785007312125533604872964433845704867828931952315117389601715858805797176205231916978863 +9771708561966315440823738672686475996359300091791133612159610896088743156170335643885163215910168604808694162494306049091846493 +4260246980990607411798428730055796631495823134395302589335824515261468702425017396399121744052394058025204383900792732372609031 +9971700068769073722960546542881880186840993544233005529300285659415785115249158554970710339057479784757811267863305208596232681 +2366740530644754206054437467535946438364093779279894170145358386159026725061074970741778654202207224942319105808241561149108640 +2131299570746273126264570482316141085118510380774029845237350007337531371596475771574171940133305454244596824947855809667756017 +5149174518393308544163272712602730988826876495407743839625377436405836969824290538418225557277170446225596736304939253815552458 +4121823510749511107366436348094783330267428964699228112245771169272372131739184081655990835742337126629817378548166241761588650 +7681892302838328396599913751062470874471102029977403036886581893796472818466025775981658918091981433719370508505357431231485600 +4066251794397726082230815863792863498887502338665772599923859722198014368910671442996024261604600584351270667582157099044750647 +2453319243126941865196218141928398513931210505017908632678271906294188175528244273588712226403525451579574075491526959814201424 +2165992720884571403675858895697135732268260556618466022757708781513389984131289376166641651464163701439701648503883511692095752 +0809953098069693344801057772787094818934098518685172639722513866511171301116371082267224802273430384579466957516125344869179896 +5481982401939122614289521472353637017126771394148320518526784723238206085981641590886685015768028048372274104457267692145673344 +2306442226244080959840391166046824665212018683995806330612693877379188175903847065416666586796553782166003940513860649019698449 +7839622245401117642004314710277578314230150041129278793586315748881510113272583096733556513373523207978534603403670731996254473 +8688617881383905361473235374450468814839809922748795968256897565635364988983320676159901781893744135189821124712699202769736139 +4654535121453878305432208653573696687720659331945282280482164376466643366264403854440410004602377849435849425047260830549050573 +5901354141130553186089991532215523080159611047438619487077531580624462005235622602720033994742060589665151566143947383285531360 +4404189049080879563477017517544794830939312853525609682809863259313640891722874703005379977055110211171023836236430564193209703 +7229494566794585131912570254848123871703487763830158261455284479338860024826943524410960337002804517176829321597236274083343832 +3895863444104818475102160062282466733057017625695711453552783055209411033854956586687106898952724582542873871156820426292736090 +8900114835631317986947314833806014993742344316565623294563018259706162592235301529753004421871816938486470617127313453996556895 +0571759514976034692556387423658377978720336611483511537576020419517170544073326612329641870061597096528288442406290336718658618 +5351965547349291678130305501394296379111242594063885471825143788366715324130022316640730177505334429253870301505302005277723169 +3916717817342560503418340716556896773392571317597234277462755462162266315055085507027881148045053452399408688937434362014420860 +1395787899840181514485743703752177228078579014380638280483962491760489212783979634460850686798659390084425828810746413759168217 +2135670387347630269927889910568863392759999570308499518950101510541542088393000686133918165399672601047703855590903413630319231 +5402756885639965603367270976348755948712799599490635951531639604227412641095043036804481527401401591963544493042050777198305908 +8529853370862945970036024025436607369800687358272778049961830381455619066198750162208786259673987188349784621840620855238037708 +0287160171661419657676846666977667625410411272514035315874978763125830094141664498713238305106795904657260444752711842879767089 +2429466318667908141926838367968922957966923427083942277132655834855250930739506120606993470384477050842837546690187842884625651 +0859078343456062742869073421013869747003592646292364644445549193378529859368317830004107789688729965524376290149979489394466571 +8035239664854439337661491633430612363668270550373199092404545748888325504908156365529288450637845828980854077097067322982777549 +3141023341289995552305686646515529412648513395018325323202158482204644747543553961230472458295893311161675174437937559470576749 +0847147233703839410907356117466248368234947493218430919253855840473886381053247491509943700747999091387943188137392889875056078 +9027561924408681103294230082697546064172946790782404069187548612190839473120786175725940158419889526634026805571973842865999326 +0264435567708660940819192568711718952454625290701063992984178907184987617554072919582117061061638475508286749372288750484810591 +3758876219081855315432362136232140221117526750802434000370402808679995451358803624973049460963970344708478212627401394392324314 +0737438132749076462394577606428117744360361527711020670066707010174337101948368515240770186600234895342764130573542724774509423 +9073624026312594849755836859839861773390790206872160912057185754009883091290659157623485315161215679742322663821620022079418138 +9512292856680689427009039672717710424584681275009917377139164236516131951125126982668253717705356666975977684408510117347264524 +1614244135791303269397434529358056292497647554306953362312956458703923199969180084694524758403500654442416064508250070732846804 +2519174085201627296034372830011366838603028175377849633378995386798621551863828217907888881997187873208215147973680227021049086 +1003834843003895903348617625671797354229539053440753237068065646834526921585552402882071062806329627104734866652786788079565348 +9629790405359972301542148043545627855421437234097522244411526361848299030888697549739412058687417924390455152178947938956747613 +1220334555078453153775416863741025226449115088431256895031662080047877074330240958104951191074477627437218425259277775462127879 +5330046177874455391020429152325102772616525806909792370732670726443060748075990477694277944732782581654693994482617923050984272 +3673634429035216620140695806733909073383802026068407079504394523147664262305135669486209989893714484828370038683949845972371071 +3708115456116104837959577481370904415573625213784335620684326306607428751455492651914598239687297810388689807875911114963544682 +6275078866641136241403805509431449819259498303907303571719921144438991991012377399643498948247855847742996423485083104296357966 +4819771171509939007698439213480827086866218725106376148904562704537146213203839362150271333692094168029380425896012121918182027 +2363813324380838896838697582331955348538206296702138518105826516120031079298034769024454364560040544650658193748994518844922761 +3584004325313624962617797426518359904557846265454464840046448902633251444504148633006804302389871871448461320891471777760471406 +7015140209188781023966520963877943044151556490498938297548114159848752913311985584190436463500622710986698476383835553548754022 +1839528400463687701674829536257273749291910270980698644016289075485436646251865330303505791679775487148849253452040305315948402 +3145765566550173415234672570838174821210069239627979798684879235063388281638855085067234894023172727303768990495108093511731814 +1483336663514861984292197260930161194717688304395281475857151745194230301582456092278030099203677396095385627051965960418965869 +2854504524032911241727059077016541586716790096641727786446371669834231656716324311341476302384390592511402123780357675520817756 +1896892000036214095213174483595939108751482199845439848320000109938495152786801921343185996467957081886861283697079441114823751 +1013835470696140967938560803552257866025556771879993212750495494783316985507407134125075415195171213975980403552113090097922014 +7515529295471634503543767654884408021479006972212507337901526368761975637401627838535557010674676502841359449244736451534979277 +6386148785910089568558317754358991974549956833481352102962872492406026696207774586778061183616357702706148878186924821100084521 +0786007793408177523760641643227040330556706874975301831960943949711205745890553620154847773954039608277686547389729917607939287 +6601065186724585869718286730900507361468222773067014813540248318802263981782312642062065066278538284353552795463948608367617264 +8710458632884006689061619016115234211820116439477889276589203755014121466655765492211482958675286327074722685854832241409593748 +9721061962175462432000129864627652688757814948302475172688435098625740068901878583888711851475071264973226898929358873311316683 +4174366427344989068552152924708427538956427143878190827546384412926545762544478791156448578168011030464548344644597978114749995 +7360922452061081711320164728509310349083463549987726136205083770590452623618881507969241810844776231293351721107321616807591790 +2550845118266461695500217220306235111000226441822451555820123914245682646337932826709632824811050296875427929721177340139400066 +0537242786569134793043366211458572528023713869510543400208763370329672141795438795279424345734151680580842314157723144187850297 +3691971167907808375655388036762185591921761050859435693988855587355710680373549562866421201047569372572737260475884900803372055 +1007691946206878968978529598838997374929069152901229662680626003917658157134099448777326028788327501120348629153653344792265032 +6789665338488546383152814125714262232923477122140534949024472459646083579525928155625268840134301323030935772657800235228310313 +4868298684968408834649240926318299142611007662477773878693177513443928825272512719998804017438032952107625783388477428012876908 +4309202213419885445857524725380221565049288409243087159437144931986513177158450201360308556789291053726105045930369784581421376 +4918602058560708246713982614793257938660065153424340512931090185661823200932451770267967499779255156975310512692435013776152000 +1110183674703280644653992907629380068789930715341891132020648458097673681456412608084277373854376969813158591776142390000280900 +0445144418946628205657456424237128264942955311965086380583598212294732021966933151948426543613378750502788733253601313302851775 +2374366628960975291734051077636704217339955444337088817734748627762386830985963672836557693273405067738105684469127912825361938 +2027506449534260776940737096467772483868296281393345078755987162674894542324936567113913899614190563606092238595769318736510014 +4601609459626193748838141934701193459914056226332390088463312165368986927729066528661188656389710831973546586554245180984726395 +8820641417137341791284188339243013012221967809583302454529489453970560570507033795822714618813232104921926392983794422966946716 +0917599289793082450351085959141123440985146982836878988204562375158569611940037966919396385952791842444219407364739497415459949 +9477416265026706394163032606799526520617258416558453349947010427060666346412817629143427886670549081801335758913070396959720115 +0666704035859146354165211453275694040361967505403921624893044875089069215309652369723945012113209887458307287755360555234673115 +0255440161315757825615697585801039525106439960924827488627325282298838888854341592966844608858745754258454784788330547612148950 +0874980077738428750262349348089941932765130462263947980854368723919615602845763694707934084120416138718231554631967803081258409 +8981164868907222146030105839525610946849494940635627162577865021292617220051659027789888586435753051650333546661672096178778980 +2620946901150871449077949420947077759285135192780733116251997439655441528374111538899479815498280668657556929167956071921060805 +9030260608320606358567118378046750979991657572292125217943471649264668431650823749458443029769965695844134676138947512285494656 +7911546964476705302214543959264572361084543166660831362636246720337948370592526588629297565497381903059061404007654352003582699 +8922075574627020723499343655693848239182605712427393497956424335482790840429881008002039906918138130808630673506435738463196394 +1030007563952008786387120762737476786971644512140825923263748032598127575949995761019498818566016398257609354069772389656925099 +4044422726596923902940860834745051450250943536191821985629944194928632102239101274072099618807513448554296724965250996986364924 +2354577685813636309146827729776859816069210424412883327405297035176569137490665953223528983078628177275666747947529655966331575 +5477299131598928984485290428357889109417137134680518227595002407302611193611761790018230013945563583265000510580955975339191446 +1619521324601562573756521193045609645344126519940152032470378539517720502293336186421186366939997498341179560168270447355950110 +4230306307869293075421899272149696843537151044956328729562855283491975528896006550623263787552206307775364239158496535813468780 +1611641802461249957884380478543784809385839965522996094364642447223105495756896318541934272292804367584843894271046287233761298 +1709756805199283313621618096481348781674012787506990084789188511001769795898381723775624988249883268986469361054705514036409475 +4042054305280705588082843156563390558519428096353822906407664058875561059644605482196547535261726903242582076817607377960964154 +4253860999746516433043460107715275174182573491654972680242324213294530205657678192111601960601634649905585928529798955700154076 +6676179068384501343541631038132044675296273720164020679619589492139981245074167783663095317254579357724910508792547067262588017 +6293884180622784793929874980896521285454501177558249542530377203281315652064795385736617567836645686619617679168909645709168097 +7743521550856066318163410468235782415369091035062393314878499152138458241235674642599783367386177248502506902767835896501146443 +0483794093371674252980588779386498050198274197464907995529503130294921702208337296889908183028360175355797035085155032755797134 +7360019231801705667141478490298073274830800662389220682649304818227754575701335947775670426238672520023046096705399683924718657 +1560373958818879850684156471739380073353650802319300382801718291634025935869130174150899754981641558303334317282125650661935271 +5092493350585336373714086054348812631829949972662091014907535609939068169965945723736778889287001972766703372105697934518100296 +4734257102525392916687039288087104774233931626636454683584533774360103845888157407165794111547584642769928525653912147278806542 +8822290823497972283964354496248019949369662678090787983018405519062148805449317563743875081526769207312318307346979280704726453 +8280283626279949899078861531461602097441638970306234787225062938541682486544091938804453769368426982357391305981842832099966845 +2217356720554468128605404764499852161966456502338368106620715847133295711653095826628517163144372167241765382757784888551945323 +3293984824292108860635484259719926152739335450946748798660592468126259449417195090268999500256639254414418461654750409028017288 +1675306289991237206693384004427250238261655808453706438418460194044272886240166511333039026240609926117492662156547022216935277 +3798683710262055280473341604637975853707715733359848491914615475081846941708688479067522988710484836724298291195075742855018190 +4581874216800089134728449476207100847781237572690983431502108191722970308605570690039979318708286697553630513952137757229594209 +9413803628857147469961583711007745023208883952507321209502032469098964348049914806057645578293300641835023243516073565578336233 +3421265017714445796604621284137835957308268098924387669789495349357687107880833828961898545436375036341306978848338980923182467 +9343222636188028392447876071807038373006670961664292112334770077234449906744690005047749541517081971030139237186089097495754677 +3197112368581263984569948101844452284212008325999133835851189833940770803601768180655910037983634430639371254776873860967944989 +7762428156063656052457140048976461668407455298961839410706956245596539166362271685772723545123147978715000469199290892122691240 +4878440976953031234401260269958754462435834653516282136155912652234992770322924693740152133726104201502258366755195941397981349 +5410566039278422394352444668313290032035427902945347706947962717669034134093986739640503933965401015646443482046077453432046184 +4739388878693555231586220110918786353416678121114400297535390976972658914130824675010539898056712966138526147925359029678913230 +5886358316255011245098615464017615182438190446564591019394412368509357681569397482568515821423340665852760995420364289038278484 +6311541503127681976206070117236931568310981280275285971795790489631417369196648946460067342315632515195328396495936094906795027 +8224055001487992245066819015245704964683485501844381492046566836373072408682020849321689488337660828971885175876650163928646965 +0254578793821590283140178222280457619069765363934967602749676534057099985483967828656474230988014165618526645571684624945648693 +0109665916508637725280874977575865530242997110923401414431409335639376452646452851515568924163798404754643876369338378812431753 +1218315801760060263870530314784967603071899867258461326183988209146000243519324558810891158838383484826158948667409186010480817 +7903203377696988414945216232421892536173370183150633896237853253925354364816894136252089105545529048726749085139508534886223860 +8148078797101770523716078377874332667044042558325146665083596936734559299430024125955358519934955785765198216278595449870598677 +1116539363983086722722201448783346777178486858969772190694450961667734803002682120934093453206868817575132463247817949468716367 +1637275206589839665316460804048412011573846372603155744795091025936193454883889805228527990919468358500076576823231506244498699 +0027621847956067398935694606560509614067216464874695482321462823676120479915649013012054108931749591716879497574932046754706928 +4550965358813639986179681154209780873107404530149950214180601478806568724333935291072801881538682786052141302073521529721130768 +1636835808005913985865949033373364692120504151732972595493618693947796782831116462924175961942781719381911750675622713009187832 +8144555119970713260660826605512619993205968791014438812251863090080817574324870955751027471809521051989383021792494139844493682 +1585931821612292754292414253292528827165768965683611240737666722015320896218326640019860760530517054982970340862863680710846055 +6536681595996347954437568694119184759342070677456835801973881107420392523103849305532249049602562479138323295711575175876515640 +3486443061013741439479986438866229511864021882422370923187276377158037668029544320414759066219016776345013936686773434186071692 +8652668214037751054395752355562459984977103091936105931037362668574357365575540736744748684242434859190554622399068882473374610 +8580093375714553964201167764812992676992679093090175449230495342109288752328986692009639151993677096258635370756684860954291469 +9964866699458956436463452444822527184213143978744043079921280767307287341419726065990023057954791661193301961189038570454182768 +5099791381798674690627429376704901489902412978407791536768610182213492340739402802196624950327768967894658802241237435134810216 +2644824029820305111520479567245295948221720974051912855629977767969256584700072624737697630748957013069006968156707232150642177 +4694014156265815259320275858444952794733981468869946419687206201356067556770447737925027827372686022309398793525531551762030508 +0692322520193661664579545021783669513925271024381519716157761278129858733071273401229450196329877841294994307214799190419756421 +2529925924690763185785959066401191385349387731434107358942442763904987402826148766868356311114428295259838898928666272489020921 +2079494677073945124622995106365273958525412410850501502163733379133554803716983972906533166904636077433873959124786425510691812 +5600448283577862312179623155268824754692832396408303848264207707120544301021495705368104362648456117090809527815889327217546635 +2343977389207774815138566905589371213682572216665369482458860895527700507201800331270809726620829481769123927780354816652809008 +3857604203317914309286408408516621032904248756858927009945259813983765983907916658284682954733130678932128039594166445571290526 +7783506576165023166951251072092917407173251744876214934915430447355544209919802690055207660666887916247859978935839016078615261 +7480703460843227445195199879222716960672528809567298037159358077924188280933697924269515298429095424116709060884750185401083226 +8717064476124069485152260308142874514219920880901182710267169211713742088964956637523570820108183570127357202528608391772392065 +0183762641286324428853776063863402233453633712505217167155150999046272831538702513532943824820006210590058738974696140094824531 +9112496681140545311761452054846654072950289614507391281868179377997604366151409215985425553727631766796511079991043874131555557 +4848466672408480575274846878733482332783913190570793476048801496992752228173129813319287139570412963303092066006377899503587208 +2004264846825219154033402067536124920004600149381068226608766267724446119288301005499297409144197426544971420258082942394022997 +0140320078386067723015618497818762945156815014583969278200840575341177014900417018827299383394638626203285185921061940008032865 +5962478195492396384491797383201234602669716423445444477699550016949258636112731978273213688262995924599283114955724957765374843 +8311990015515249068920407871121923281094596878515199867380540740586492915851905851867276732161963585489156521545932776243150499 +4592710682075197276504754152068206668838563346968654451648904363538719952128821973082368544846718432978965867108316589932428817 +3552163482893373255049569753046229304178905562057146740570805257427023711941189943044300807285165026750415508222049580394911287 +4596021173135544782173915901528349195173147664807647330846216356295629295015969380040305219923049284417801673254103299322068397 +3943524404368165203936926893260166313850279802117822440456770151714171409778904837544726372346574716859663114202887576413543801 +2969116285326615235890680734637608834483184611588075027596448992877129841445196831709848480423954210927050978003235143506132046 +0153071245386120635128957595884412738484038627307517440237760302728984006580096280602129668935036883924996553872412205753013658 +5621357475787817996023091217050008449865559719579810787563038126520104376758261418798236153692393962370203265309058313384060120 +5031974571476436448786217419749048851015183003367752606459920605017140928435866977820091890996541266633748940714886983142086133 +4562696779266374687250665129144508120768763103068669726719903362264420017240549417302081941962780048493798236161080017811934325 +4744441027688831600256636793504579543928756869184371127623452884135869811364793667736493944686083992892480103958881983194171487 +3993112528998853349337977120649249691278774025202103634228484270608254195748299845801769708524194520077695532568068092195962254 +2329576931503118880223192095103946522402558163495818627264589763485690044385772668639064805859345298816707780560479994433943363 +3218849814933855133187785331003935806771232881004238706522431511966088657254108552621744793383328388740747036182217129765309790 +7924052825089429554499360417535494167874442982520963688982831955472793462543386236218475310833559387701937594241052466519691317 +3120730387098471204669336934308519377296835486747153400369971745719675185172869103202802955453713945706425356726081359191431362 +5436861304614177525957519069526164638577548380287496823470807113570761415650401959363470580496567502232543408020661206474053266 +3120249944908567907826123413136257067078494146549111473184473022022655595636232845880738387277254397911665634549126629364411401 +4035843002062617386757564723259476788690970258752656414372739557004284974159497458955655696811845766981760932359601002427208626 +0779143969819344348002577455948300927630939121921547040685880544649850641595729872650264658050333293212488609440422250520307515 +1460705203029148369614787086632962281691805758071437499284040866802949156450922963941143029043912074471289441815938231327288667 +1549240739672667607945860219999638499322190035348564953406860715951588472937396572245559080251322668950418605324345241657646723 +6681600989234088565629106267156100053645378516406056877177467623317919074902879430656553383602319342476881970725454244483233156 +0192568263169645194866570376494013537343370202553601755904593727729559768993904298684689230055250228064310566719259142616384584 +2226698070135175270997371657677443976168753242911675302780266617573092075405498205119010526964247236701457645067756244640039560 +2411523124866541301070099168413071292074360672744530325093983003307829914552999628543032410966968534244029432252498124255869721 +2032148958117324274615176859919802414958436537913396718375158773684001583339324493639015775574550405787223527127839505198335838 +7873829779466960857655228261450384972633464601331098096364008012962199690499598210503782863578580915221775299630382906076523471 +1033617798236208613493443460691499481184470043787180389655899698166528830636916915480716417683921961582857940369278335021557721 +3570877773431086041166388678403654768190776468384892784896107473099172280071916265352211077124176187206981633533447143541466428 +9162337956051462461244081308639454192982414597589520706270659960902459420484545944118766172576927213645046647438580806130370976 +1679386001024741318115842266812215689927333532837813069949968390429571894735964678235628716093113797167330012932695772923859921 +8804168783787089493714870963570663419696958001331927763315516123670935571014222651056237882407840332672169785104960709919825245 +0497455776366854139775882322268590872937081090128717394425875756408043314452495438785765312873239203328867278937236653265323056 +6042409906538241575209979144212026252259767618647784026916367882392586982801406891358315499203056053321982052638011533498889270 +5665573721287635790869699668106163302621832934471963587290594671222971786422361537494624465628017639194877871599798228379718225 +7432584741479817366719910925645646541846360875945358928938495570674893693392242341873215005147647843928108951666723535877703443 +9626429137239096178611195842928172193314671253797016055524388530347610194243157336306488458466944807366840872517364192307199510 +8872554743012955139567705170988677634651238832225827058388116110755496007697973600003490415480515644505946637930188314840631970 +3992491991103767989877164315291928453885661238942961179632807297112992978760631966972677658662459086278724019257889314597469958 +6669007755891601840756776309575869340114031142955328946684465711503263349456097268207290469828978418533272473362080110971119500 +8033630970428066315841260643530102154382783569132687157611513276699429626479061706898110708339849619210276811595853043955446869 +9768070537230217029438684537825935225327148348372819431279340284261370618005491965480298943126300044774337991256135794900355667 +3657498279192720768668603975934793465689868735015232170607621866439453389115795388712792645934981304662545764256708338815562273 +4841652997648921227154354900004136760485517640549167386012657306424618486993618255961405066483542868908453734101505226047480448 +4390654153631921147807124887078770782524389315313338172327932505555766712042126014521672948387933863420403298182271156860089870 +3083699675349781049946715849913203260143681249018875435801318149856990157414011015651215483550403032096067120964528627647289771 +8136660832504574374548878451558509361941048418373428464399511971079202695299106137651197289051575362091446283996841312766684810 +3676236989934685487277460614965631287879882167402767160319121856718994264197563392677953816932994293095294646864998371569945650 +3049112960977618674809680978004704430920799039334511606915199457270782728770389854739859266964607398443304578468934206463864162 +4020319901840360451062762075624924661391286802372025413278593393390727656336661248466535685907382226627976102440776444559861524 +0166268182450996578449973258577137477283861283683233635300267518223499975003192269667287508860009509128730943175015750710725727 +1027094337033962463834235326303117245639527702316027545974140882034976571260270032549979726479017186330571896766878727628563084 +3735720519280562035416027535373646170800608954405500868661244053278717672230128154298018261865339340499855937060742071802179228 +4620909442780453001381207438741771581539126274686674694429903202199824281873555557550181182902700089108021432193493732079872184 +9458342180011256500005493355500078007350011604607303307732963471459637499551951642174991940916434214889720629725398539493831562 +1757418742866953045608344774690257090125548479597625280633090790836185148048976878560947512134108931123436802430288121604450838 +3439908217353866705662826605127180864607977834354266960510252404933627640585873170058624018410480482791416937234522273135497031 +2172788248827330880258621512418568877495834091216163584166603056260778255791670347944634914745463656259805211952460022859276063 +7062821874356395565274839950114991325558841496177484931811683058289009845398038339874364587482418953564120109998982642304471400 +3698346712015383293895452263862425925615594845734737899372976339855075694671913339207356770288208509103055437248670557197975416 +5731723926926739876475885511154531945948200793682997616742493659519964588297814245753251250515716831733713917292045440536017047 +2326213034490494256432217527368652272393723129996987975280491971552239548286262875444742346718137588580994530639159848308302488 +9664053981063208567695237243202681506536792559295775868533828506782791916794818915656227027388965683683700700077078525885858163 +5667625010472277345606381058073451841188387271842197689932469281333817846476853011214737420944488058943262260113307982047560323 +0376867635943376210756229949512175221086640268169558374511241311647850185329038766762763270981601390818540680562189733654333764 +4491518223112590691196826611428935302844272864063278173063473168207705976253698032183297470010465109985370388667131449602003018 +8802699827748011182168113988637297474486522040228085650948530427329245143454181410142916723537434758973564869412497956238710891 +0653015424586307910784932154517633495234468902775642895621562573878654050393509111804824221865060266482583193591594511748836623 +4094703884414114132180304529532880233548615595688135936066808466092184620343793991343848249436804401054149903884653644876865628 +2718370374189505315521628763517179672356494682994180239553818915110898807872150301279550476267769560111595428023012737770277809 +0983429088572418894311039932871928790123994971756838750348015397853262244219489154464202472581023076365814977799205726076421849 +2244581657719291754700709306659618001196719303854922152332583798578459941542447742263613733064435246505660395020395510382351759 +2371191845917148718985406337649548363315059193935226677398831831786300690640380203874437098924585872141781137278459673035181084 +6868095538827304671263059227883616117642085424440799227282508929248920781388666797060697845611588551792908329555898460456707821 +1192043306305230972108032344493739345654505538181410336224627870081996078504793311999388842368948858806146981869977786056189228 +1263564157432629856155576816827336520937607635620996781728277895852539790604285849253279394716497232033008275892868545867382681 +2337190119883945387644191032300914026172189976929618448527119917727894462635110227775485639758673866947701176216601761844984677 +1541725580846818676211322948610609824222332361334426723507920844056410476870968277509960759021044456134551342168799278225221031 +5808026746515835625867232790523509089624770846326913802756163972740615072842822135746471264238978131846218179577994033108366459 +7209194492748333961784669338759539252535168061991620450390411453076684475148292681430958283119646863377618784660018401847206388 +8164592842234101218973444204368622348477154717654721601252474279965258123105673927507866232148031414671644959483263113289777725 +9536506697075295060127905565119738004002490249111193299408907722018709064690369235720344207608369516256550493184611556526526774 +4560017960261564009218990204461419858845480963206874732488142599183336196461705771053123771553268404282868440226650446328616213 +0784979620602852570679351917990832385632423349482355239911158425540111723754138128518596135522138872178079847769857439148437200 +3065506422221326046922349934844525141835749289303921189806839691414799095653902814723672937020068076690809012734952484971091424 +7531639556490305149862508189711238391327019426642515502558468392326082589336113833462268524671249515158066960751008937821736410 +6252414527138677204351141055246475557495876614593229376289610072418141564580640343324426668169327302426458833085549654617933468 +8091658367794443766667336494599463493723421496560610375457662742614878721712215614199388402972271203457264117746009414601292472 +4908066830822438120188614160836549308604180994779262747990121233555704249867768081188552478880193430879502515548138613493765732 +5079293880539371206967935767115987717076965982796395512686484284292191272158775216705810765026584234552773076439527278292069704 +5164216628992440501162199298685532131413675395537593848742057924552014924010292705113677909421661221880801679108009584398377793 +4121418969859257717214261444608216079650826208617995799712962734646420115576080668425510442736110574190180643945326397724337600 +9884581544962427545759780318699856274991305713483502351242287264905051143111943618139482715140793163760250713362251924403750737 +5001181429052612807248790060512136898354370778167327719757330132811615985502470639866207141577372460429670627788129417134404406 +6178770653157619944192283152223652702640541977712224400210339303510432403323032560310805508239002669348396267104479169736023331 +1565506146888751623012576320991779119855864177643274705087893960131150916233068282254723459661688282729513190349028463435804527 +6845488200397241399013882436401994250911820745822241059508913459645459567274421896167243282273985655453126249904632199335837792 +6040683797579452107027216845343063524780614029426886935760791257030864245182939630838108471908303610357161096840181118609152193 +5867358422344899058837359139467062379239965703229659786120062948805736192330007221916713426793575162507137089954539396331620247 +9120519760679588392286105798236021559061854045122019266108300186277736886734969411204978788065566311792204801446141473355188648 +9729762487738362702158800023376627553944314549674371461896299198497056817939283617350417487984379973531521250384859390378780952 +5278728682099899168824382639886768910340224230494671557124611921288793461227571446099109231672351187529971910075105296077357641 +1730149007222759960804235202582288759192771252526372328644840575023003569331571124411309306137954892577214909905859437364845514 +6359722384817229880817656694157685568966235098606153131789136445509767178564237103959982917552413501317717319150118113725281168 +8331881891680259180368288807257087972744816443005083134263401722243486734946708091896751081947017218318643507133088446490502898 +1564672123742228001093857696640500357335628593848961899541997547484403147074041514838330205353223636545525633021798226061157975 +7577934224848992516208537526305362086756191009198436224096722971843979917310448903629548517759776409320845391838217575441202030 +5842023675100545856824292093806577194777854067803726567456782139496624136800515010149246416535077787552986449619329516458032493 +0373167808153255052885634740314278397690578953752487856466779556596486994777425942109945709709335720717497273570846620327341193 +5952530704523306889233492671665454303269886039975987299656234612350738474857467451882170147036566336312226535534145167519693735 +9607859559588540690534618223279700864577721437927651039992456828233467681820892987538385235563873550538138831743319323285816092 +1624406727909111147099641441681559414299104318980440722709754783222151032344202663364762643657625463452804121615256537142718557 +4340315496406729909696728430969897055002939519672286841554008064322610667892226345008617230358420658804502966997298469146129669 +0210625023915241926959138868143886129423564883883556915150345946486026490910420790564346970440390087533686122782420012907572301 +4721491005031549006749404748103949153294842924575427991607317267682247725581223721840333859898813958860082395329629494670433269 +8089333321693829301610669436388430107322213493559809633043551047784093889175488034859221042528763690657356439399119706918708269 +0081737607011167672164816311361636636595718748621290008823635691873373600228901339494989909273621908199424676593460415730887285 +4160520455923507310701763781457568843470746581847988497318956296776248312754405407533119008905656107514741346097573287623338123 +5386718227154603611476662828982111704301478482540378612558078862521675877095588029121004214011721936617184276464093409939658461 +1979208081789258362592059412017416272899419903367802069424612928998858612300695288310086571610845555893261171864325093713786916 +7319139333841813731995304531194319105663469219744020376897814143833856662869242932535917521586466923358318419175396557400347006 +5043921771833079685531325825310432807800223715751775658072562091646020835717475241682115604301178255769370561929846716138929898 +5795009540103191080406839616053879828656176434583781287476876321448232920090228345539065457933826027880057345811966575002039185 +8576153060956850521311077608763009793771506275720974549550420722202837108735329754654396372138014747847396055096136010954168085 +0509043015950572569270166347532451603412568949379675617083809175396443490772874366901432037667793377607725671409709046037414839 +5757807520296047868933446233650750653295440658940733452331643306076758589809542688132352958487696630197185077693771148210415488 +3493366273573202706880963127674882710949035853748406509260478202136097703296547394474710548706092731416204930859887260446245253 +1707539941222225775800658254639655291940922607284340086831234164821348634212665200587884346716985605251686416602230060503804746 +7119373075229566064030462457605177717171013246215489918839015056056006164706313041702800263980277171617607329962257981016326162 +6663813052725497188713444974040320318581428866821251405183260930030389032259911879414867761739508256774997369921307923003380224 +8543233997255243697045277479565692227756699494957952164631865806868110331658332715068585534381871375140656787606232565908699795 +1127161017080656837915924804407883971278108801828959672411183897706979458144168384325785293840525608900932906069101189576035960 +7746884054099676741518894156142307630763897600710799726835033615686726465390861737711082117067758069961050536090277623500309130 +5637631551447663652036022243883777403508274485804348060822361386569981574710776219228289288499523332852532323527986372935987772 +4799128874055082677319044677884361786887526377254867769810507962743180135340211433750487850964619355570684078365963703331785531 +0073682589254914802156389462761084682583678912826358978335933715271645287806405047356166648799977142475160038979372628423949010 +5037559719886724670132444119093240447382541873564218599313650731701908225623880448841585926824993839038111972019595929993742491 +3846935441857871521862191302228658206156991884533871767438656661539104499878809823997230034119838267962065754111334791148146756 +1506245754838722163589450414720903186576980445131544657545019758753225527419416112605350003805887953770147934624442363883801265 +7711965724693431682894274811945992120534708309601562503539815868676726704850890815034768784240539682384600476706665486329906626 +5956050270087533659778978762952993742209890928275529257391035690977242967921950083594415755279086778292240063613624273000623384 +5061387982251874414971585573609892535056862802605728035065909921406395624271235709374930413322182966464210152496167481038893673 +7793264955081367428964822852138933334974506780923885826935088471472828950440677885279984646325294353993045156334916727728658382 +1917731844388737433964393775321834810791126648863902100900130682254900663770885502300591056044829585219310561565900983432788348 +3993621103753371559559083065225969001381490024230870158740460349214225265354617541796726739208926003200650331011617584531364466 +6446975200293349558151403162258385505474942322796455463090381985898884153879810666921247678344495019592171989327800934238087607 +1116773012813841761179870425820266292137052144431441290909627977940969824030291459806507439794936788928214828707352139273391574 +6029232735235425403035383736905446200540026716844974571516139411975848154720481565143793111521425975198184192780712740114138327 +3438200310045736349450654856319455355471806503450945057852390828051199118282010495739738213447341204668213941183921897766832892 +9512957847613586351781261772535091586107882959218997758350989715296118346024725200133884012765597340862400507624462524830866480 +9478843365776113648646455826476363854336443473601804016310466451767185956320296981753166811182992550179769973329037834636002638 +1745421142947154461000688129964181745046963595109768527672990383210124171587972151958957748633651595231193536560491908213623383 +8381825139460307549572877031135183947681591383140425137518397676071788098018785187454915609863724531998066262732015118963304139 +9818630925621801216199389879103728134389152002296761687336537102178195168590959317811012194485775439363610537061412149495648268 +5576817033233193054333141148999572966012541792116109714940451926593836005748304065707002129553500879663495665687664101078961830 +4255249811578737819490016920102133439336759727576849642889727534022902117660385461887412305323198051682727105599743797538768483 +5664318936157823403995526721241574938566862585452228404066914834889748254113822291330168260243598542768358287033612921584775608 +0493408359374626915181620402845179233550927620380852963377274566085358451742815682206366209318504599243115355217504587070160767 +5099926458337087971802835525020183140934847289656680603935783200084783016975906879446974951582977123093907468193624294378414602 +4993211036037539293757401608261712055642333911428904305517543773653002323722539705273891988338327482438288308063770111207012273 +8875724448927612453412899117913683062983270974383158999932195926949387111141855398745328636343443301765332909049991525667183004 +6179446485196106673286301271613059516541037127280321853066460858041150601008945716021915010720764523897185471929927560029444239 +7849437984569538042541347312104898444874295714765888754190517892771505925662567504469022216257045253341087785885957187833718309 +9972288762936380366686950714951282499790474803745721728891585856096753079311064364081796195123806475478622506713826868777638697 +5194143783169339034131894869316961917794611019135131983101366014715857076410377619181633332553598229324447853163042312520962414 +7850854551539305001687198329060780010579189214774936469033426991594801002823836027328068965959941131816419243666581830678041716 +2062179831972241107526467338284038440066567441767013516710832064119073030278681905009991848241324570216334414105094116172288746 +4795251857046503954973485018229646234695366724410250031504983138843493652944866619750102836093665932368212191653037534272918686 +1825362974498797050137125577095725107017850998954189361286965048355333973664280494182300562942448127524039989758648090694962142 +7512994410145549835156679857014752505125401506747737470958447208937721056830032278112608932324610937928836512547690813007773206 +4386958625254464154906642431853347789932184826155599439488217117827733831342079710541861559451261895702289359997911170346503767 +9160582845194443225222601665399912946931088757458941216770145834719498890068834370787508728005299127862046720974369218083539582 +4881031377785621947067985825999914746359269429111016243690638440462655472434228579718379181809439967478754245522265321625889814 +7583391301685587265357562950095622866121830355803288828241373074939869852539741493888550714334780322503664271906152272539287229 +3706056275918240737054417094100085374094932331005897520832184041773254725650156202489186855782648043533132404772816727073155235 +8593562217884076811451277453364735377701544569033315203869164836320799479709306444027657854268527295924524538225997862678112859 +4570537859634341717395527618756161556420569519217630387646444203036568601265546935410843254883517475649241288633895824882290087 +1279603976070610116464640212199310166444520867245963242449012132718994226744403248200981139871780434324112903659101791778621066 +8744850384566117283317344846157583437411072049976031321951124766354609384768681179440522110424899002915456603055956128713981139 +2086242661312485180876173221212851654741063402662853918748324706267356051777749583559232529239969008936507197332763290031477120 +5741215520375853751719868036345389495144698592395029595732821924732143612679143914116460149908254801423378398341400612840255578 +3112678009802694849286559093313827681147182296039209347542924704088172807791962585025279313952354773038812296501897230865202710 +2464335074070148432312036647984428094369519448454963043288725321796755075626414550577697084871002645789785381682654641241100283 +4946700365473689782081952042866282556327651210752194953923594323433725250140138447685850292340132652464859222629234697220377811 +4116813834263078090553178959891950239969717708797371397856902146227502290357214553371450093318491311749632101080525636146301695 +2152016442288947698443136330933042135184223974200115803798042702098262975256411368585240157169341361695851434355296732786801088 +9469700239580500443632308541309983452163906017765379912144038775472527920834490565234287360219977902823838503195349727039831496 +2028576502159755428471922748239092215954614499408923132851493814161260002491760119779606365853853991681827617094579573055586297 +3640678920108595929213187711839271941417061119448282180155978731064884982006841462863880420761509974612500984423805360525276222 +9057118337335912039094779552592972943649024122835147704439083152493402341167911887606112405528052285968546122798145084807566159 +4304613049097528789015322647077710992450848025235239429386820053371823148160964657760192249248892087179211556970604610852657330 +1245963067779593226119382101475675796217312455063935339967592594066075071514347181089457321147516518204553844378465193273590500 +3560863140415397624008978961463819965678362699058645101497391867248986869781266285523093686811424022579360460735505488281520907 +2803810314481228865478572362393166113955034570401397318410902932552817985751479490450351950758398971928389088057335245044158094 +6904347745025056517093981246275608711548205826384671869194152526627350920208727208140128575295910227390194690418706771705356517 +6784445719916791606648643520294851397681933118673503958741728387886357271190905065762821681465016058740202568162846967243295973 +1820495748778970925066926664077661706850366307799032827973753165199895444172414611070770446635902894354703969177838887030507999 +0792366480532040444153327711554056488308633552915589047332411364711058132183546270866868018652349880999177379420796287072716088 +2231884404962985945388134094475613555762253572694848559208422137166239633417739879274457716224035685193748560132590653034477542 +9227302311116220471908055555125965124068362598930072116668063472712158786276348904469695948132587468164071579543544049342283942 +9611661683770276849635190630772488322125598419492636769106569145188958898006930644639421498885045868655337164693651551682789719 +8113170194159795409724804157277970216585110875118496676005194638884485314135820103479833056690555003499859437927213123666961409 +6776248629614262117061005539488708741664752240716973399951870993215840911122617246729216135676971155075389185544568368200715006 +8136621653681348621828391620654330376833126244814473075974634681828022721817501734663865372891778488609175456672709780494092424 +7509035477896901683836806362847630551058479464889625032860924968434340014421918549778761662492676219077042005549667470710873708 +2649270641258444022173705146602820338815565069036316996296828717613516424116147690610506944602495068800321856748967379303431711 +5231537094318273489411666006633698888790246462698166129109823556983405782712006150780252324827311704109848150151626541203089319 +5293239066624699492800648051071066181862007268310279648019183315312315154420858012591159812489763989269552995744386956174038764 +5180720261104219765835445462051189983679962609760132661239453035110340712611783326617205712026077627868902275865901764502746369 +4450386186789754245435591998462910276602998139642701382886884245787698610667584456364442583085157908011542453439257529663938908 +5226947020891494666123742174916211157087031239651884379910014927950876644487562604700114503798045681730239237952766587287565756 +3462320891328376981719619778327669975981463699579856289946252639770341221258397441894692753073255239973756891174514808164393544 +9266128730309970612623389424292152551017956433451773715730990286170012828117563840968654996015507568606746254308032751484010172 +9555122523637838569715536561794279389081447703619598960381475663155959964617699927258813445644496349054324074619586132556815203 +5221901221258703935673357600398356681976947446424967337708716326590353253394660639230448040100077013280765926948098493319213488 +7738313289310543436800301466124075949448435224583670124694327098481119456888840896351512543815188557251685118660772588456625224 +6115002819234894874533900222654850689485882307323315708374429887345170183105778321445910604513739852020875884337203588872923125 +4481043241227156956639553841037732426965917364470043436075190174090995043087977394594850901991029121373900633832712403510778230 +1122190232976475343331179295988355035324572960682589565916578492624347219825791452145522290660285362795514899273106419997278153 +1419535386080392454227188259532485710914908620105317213100011817889875764632911121751593887025644393163109071422951719422847110 +4026612711274029089236246041114948354793310083266181854689070097597525414068795108775947050469531123122537281180760454160254035 +2729212202223251132035733428110959851887971832113712091873626429082924950281013969697115103891338579818898846568239238607918122 +3944336153317752913161793886424178237035722936447760222478425805766217641600209813232221230340238786982394323446453582721990048 +7389193916919980057221104426235427350670234767999865432553198312811347361703697530913350075859737082487587469312736993623735804 +3544483515704686277538264527506961791275819582201516681389283444580314635355964056417380225809498367162186604237676145482322561 +8044600252747925830317760868421526571377975782889497428302869692743382713250361972424374300944696385157823203594995989063758674 +3762181316198228161833937930845505733610147834846550265056669736693282801787541130363631664735877621678425882651996823896571197 +4895759837384473598984792357851123553619562828621778670882704444499813426092540279529447104250292800981220546692084253328082420 +4664284555514464728238600094152484535233997991752935740228264651440639886242409459644164822259574135218995760166441434144147387 +3498021732532747332097779512063273078194846290742584872890698856916194513341236677884416300942002054144602101645686192153654576 +8855689629475186087358536061769600329981635356231999344927571715122048450556630571843667383074529954198709235538731487573715687 +0984316998018946399866843104618259659452795653432490384229487374720689309493558506727485555071518696965289338830868013877431679 +0444954460337393289812378506420855357215741077375742703669752012654590715324153275679920752247842054109194186158654335712157841 +2806861505564373354176276173563926841713871053400626160801979041772088503472612997224598155147542728678254353573523697935567654 +6201924274245126686318859488923564788418058568530778365444195042057133158737326264356888205129275667061366349202778458019010774 +2825177079603097543301363195546845866777243546459927001122927276132658961336248692066215766333309788747162494346240324193946246 +7328522607444197904276536531669234484641234327006749709725328245867037809150903161226855351918234564454937188245329779811527397 +1625377992337069927288579727970280388115743434164021261444485502332859966787759877144357204976585729746356637542057830900380879 +4756314028565636147037500545330319143265189233753100563686689461353510356901840785161069243491300815016067752040583292254841464 +0425017333645387838654294817531355494569985613245897735076448718279104158222119913920255342054852667882224523660815046839280651 +9062897569747184612738179958208393387812077029581124581459197624411283206076395360970022007778409057996215320186949888642491834 +2577588591174250273899408593063983438617592453792302359558473005206418002455294336176016723332413815891373160708758731967049062 +3790766334026767123109787766877375184100806711868340730414046922950551256206928850545228229670325982658231736019892656930961551 +5274543406306956723055597804421123472449821052046299950205504470231963135543548703265039753201973863356153745730092781664650899 +1020617263114480064430348590768589835423732129147820366131428529210137664442462332795234431300140813802687903923031421483099298 +3305606503022918455845722586154193433226668761115620627747430416848925633688922763380741392673785175993422375485098655310345526 +2853130953848078288453311632699255400113689129756388559283548184844635112023417531659479267970268571040812683475165664822794180 +2783222167931397034746003825257822695087707764404029816076797619920932960124572983887919479900468345097640119343412470086673322 +5063459185940735886394343828328918625032505938760162817991123725871771735016474675217009872180054875341819281047070252038306775 +0743585397605286027482664572990290216960312219499687628442325704541075906118067163819053997876546874075941147761887253181403133 +6865060056069818696891296915545023370983419980885799857058396262667622091719201948407642987224698259796247084724028705854417594 +5103827826152310417357275607217586307830343393236258610369278200854730301259212109525027296782910788706655437239982908254923137 +2959652192735329005701145454769790852470342092444456431448859896236135922540005403226570654335981197446157556763871319983952696 +0906069491801564153648838254163601062811138002404571553190646376754817601328289018602749191681199089353060650264442584766903530 +3785962322135501816792718661619323345882587573641791662855806452191640690223489872037123172150522718319643946771618053874607301 +2342704599377336428681571005102144288192803466969307063516540780329331225964749753260124769353818714585772490856102378132713276 +4346236542835966635872778222659462346707942811767870117146806978169322604045750387880280187005012616459999816635343308178563646 +2604219260434304257671433967264369756269802345023491459538543248359901859356108205297353830910989444923284454022447613985397264 +1822281949178892000423296707942037651472365683251029280064728588016134363373058610506594311454908111460545368768742973357987311 +2329670499086760430314104242828210716854904379725565610986919473143807393666799098016719544449077169306161547903571473642324181 +8934928646320239275117990276319738648757543739070170895539210951333884142223117539519009532014319073300363829125825739466521964 +5363386211310899141728954217940413318460520197666536176317994465965648033515827854532405714333529319178360920611572195490491843 +6452281693971710923703852296775814957821367584399432406445144322839651572894248840082293918339020670715780310479909368596480688 +6791949317913799774656284904153563675547018432823443924016298235065082832647707880299401729868016947630432394689913082273372425 +2712968598557679357916581334926350397464524640833937031346811806334561647348253048938805411658427143226876519485429485016393766 +3501555543617126115194082213265703065410803089224710636262686284845574699295863975355551722711478618786950261560124771603773061 +4778106677024357003045486275897639115668057170330533010873096634039731123238801834051871842868704307554039712015191582514954442 +5218459195255301650149970279364690486331464065970810654696223977244841051824600951397161627678662117382548039349844217280375193 +6369806374093253248520031657537241583648618465123745039819043766572718344368140639003289414418211436811709774570097952888487167 +3212569840247911007598018387084803647762488058555511319640016886915147136497977845568137947542869347226726490373484609221240787 +8115733543956480787117884715874295702599005706164646884593210924736152784838359510894224199377258864940900115663844969555460182 +2035968818201814843503316301816877369650620471604903993852843829395302301918155158237168629501277731356144310878037185323999107 +7016895419441126229181619830275636225832821520441765441532992753638148826237793875874434736200782222141244213180375828740996801 +5879711152250078980107885067914386728437059276590755707069830533352692084569773933300266007591174281283361187767672358396078896 +2103537195144138796197574043874859185879649977538344432422677102393984939164145250471907596342652013494773096173424264207508962 +0637450827987698515011845992398093937196147180755217329303542644463406470700019971836100495535322254679390620873420862364850762 +2242509562912546291760696517385259786276250406593124098460913531494124669973775469757781521279236307631066606896102762073078153 +8737688554624301439839538446862017884613896797755819281023174101498754986747558403862071766932594675733495440363990431783540137 +3643542527592425148722339107873667468772709136145667446671572809454163340131116640859776867285040887038441378816294697995941474 +4072206008606547388536544456067257224875289444010744788037863160664893445070574937489602184055019861155649531412106370948013649 +6183624181311379076807905566573341779391313360549851142539899794930473883081910943750299100861533261211231956101452244671916467 +4790552691647255291761946345206897704211085378644313837821311423673070233392146600975258580259558879486034230540380014801626096 +8493966397430159495762611418791059378501633459275242578770822154956094389019127421733416568122031957966837378624113082091125501 +0539497663700011903785013114734351822728368447618541431249895153026462750771721023669565811790420914410370442390790070707245463 +2527965030066432300857015457970368291786471653036513592059650961230415181073725654607900007084498257528783224577191374901431098 +5466092963804647249090725375187844347347973252108137441562290526430827908072841935677879142427224945383497721669674256500195118 +9408447458584058076150017650238543964665522204847767119461148283020725314993208318241105785737923682521127890161621980525163933 +7752816476751548314213662757572297632611588857400086488089795605852388674435780461993562784936271707018785480608983595358835686 +1627393802741824989025898363620676709684659912421866285079664625171969200262501658717867654051389804050637814037774619076628490 +9122449665202107140311875578301366613855230050319765976709818479408721612162782387682958793989254712472790259401394162587792903 +9264559273820216137804057886666471549044481178190332632113699758788705141611700544482989913381964978281793867160157791927781162 +7442806842403069859300613325533104981898816922110133255013384334075830779759933886533885733793824024671542171085144755776119332 +7374346360629846250873248611855740390376331086859870274251856769041575442564535703086946429812945653348220186072122845016274001 +8100158244663714389704533217051390472696914158612218153906542802690978033894796544874039962095788720080152937661496672600961913 +8620241055478925684985952252838411836616234675852362577897905957450930653087681187667293254151875334644179085362678160756183911 +7978254875299875225763061871403474457110245655385719997304830996169804380424203018555208820048813202142753962116252594621406974 +1364681806031729655441460904058555518845771287158086951978113218142925356256811479736849256168552051145501834683683179144053582 +2064837061784377038174422796078941972546986123715738128334902017019689407784830561790843652346745495974010607663653352384636987 +9087616048769273749416367719522093035127707930211676277159029972485006678648961483209994033836189245748110099847024086616545737 +2425064383233144306141397196556964739088709046393835346029594675076399505634895487000914867003535813058158157320486332684663652 +3641097021982425901903299759975001000185954507785071466003606303822049570876073457705939373716255467071337231807741213874106729 +2074354309025181421785407070622306037112442163872375219312465679573739444271473076859615411141496918260111756219972558400237414 +9209218175245343776137887607783882100053568632353169828579551734386860768832081473967959877027533280331064368843953279403791000 +7265829914895705780556070716875430476662014009364335116363868273301526498001345684876869560767489114942311375190271613882803603 +9073891388696315073189212242611726526377243973277573301675482213008892303352424672983354043608654230919446999369674504952411077 +2313808555572186833151976371065120343070868637724954931470642187595688232146689299510744666262728998553432846824564736716304195 +3769895001051711590090397709284289543423142627611293076964116196325536683515731394758243179761144577336943990391686351744541117 +1964012423430274286597296893918822905314758106425920575603004465793377045724816176059995632780349523671266024041147016652652055 +0559141307894322726836380567121144490621939523042182901839217710526781776038951549469639294413748250130589664480237146063632821 +2784427615240187672159066907559581170138189996990499287132279332580644422377771338142338877270406145886924306247277891647319839 +2650622840124168882146785089864582520075975814107450314378337065910303739372347881027629224527453006767239445485125470798573294 +3280112665923668502905138798645913537592166171499662307256909576761808318862002890184471250591748083711310397550032446473077789 +4839123297739060737950671029462682374736375690549900393153725695518603231578972526440776594614418742798429193873047497505681792 +2160244025080490634108186799676843921536142621408043650219395075800794132235618687434126288996783749492148526728267890467762275 +1151778651927614305188542555589072273825319471951036866487051470913760762080070986450018226884129812856690672034368570260461972 +9126667808863381235489349849691246557114377306076458702232805159967351257175416964221955677360994319567614203175199799743819984 +2789442446232943685597991603590204183390799819475475321518636276804097467590827293001641282750302064255269776064371623832516088 +9048597881455099698450226466082784483581892839976853703691711348479752784919013613635237082895964344395914096977829703979539035 +8807651873851261763689740610852384724724967281185663138373836249447037268804159569417135422054907833329936731832253007919797504 +9682114913920055259992108818897312911322686928213632559659016546514207889796765657343025470035022686294498921433293889021982807 +8817906539189722113102359692407543788732651010055394962852526688290033541790486992705883840600079101359528027208567808930305163 +9801041923433800395009066327257582729179435202758752590945790035180893649947139262456916818609204478389874916889421571462394032 +7712450051093466464431798727528046026835894856944238434402423528849559439733559946888997104896406027931037956349309616326923736 +7381318701427430852131274547674285844624653982181746045743742319196948480723763005752758284784444958915729789741525394299696626 +8012218598270213765952492120607042939337977067878086380794314818747633639449994732495580724511274417714861329582888877331906442 +2523903559053376560461999356914800460953183047966595580074070441846526092785101396532452282664986312641571216344907286853277587 +1243980803716723402043316042450038929890705999070870015541470833748745344879737850504389676782666386423839519311757255510240006 +0284528532586136580475966266604417479884520212063362364900502892825693493079279816397630592037794996982087214380723458893748385 +8491272727902175551882935376152929301347887913314101691264776996119552678466629354884721510455050616404890619695271496162337915 +4545341965713318896843475698632445949069586135305533687841423447686352614650216106958803696792920165610566274323872801678178779 +8253568664915132158211550322533453124118327929548882623091786963905677622475126883200587876894319717512008420758223683547396925 +4320222323998014931144025979173468123790086139354433220062005544015035220976182446830891814234515809043692298378909223491021578 +6069959232219660879097232566121384126364624848136634056377125102029351039021829959177114631501219753707218086851316623762802248 +1243816089430429483661271140945607300465854350055161784219266668283907250544685294916674983006906835039612884905549454140955379 +2940117682598404016293743763452820571055836767681621549318648097809089905651411870200800086998457828557152804031061902717793951 +0887454608619720066585775426385076124950271424460822511049331848391671487328356192926497422819817241814088663154002962024098217 +9444111199203225196678001748854632792810817288744330344130819478809411416720197937516411714390009340089903953898652786438479523 +7598508564858904067093707855688285940754985295788676773297431147947757643127210193954685014018553421494459901761944702436530765 +5554085843289054481441810390185341385291272744398367506999297552891321596456006697431455499740285520112057597547672427356435569 +0013578994633080338189924814677511395910036898885609486453589361725442884886155382037285922038189976648035996103250646536792347 +1883418355117695192895288397176362340054382375638457044029914555409870529840507997093953867543753807297378240486101911704147190 +2990020394219301141282427939313186733000711509741075546302432127423255702951507832353089573401235669455023017949653420450062207 +8315834094959817998047438269545055161942585493293325815723771181252407447320553420868080424394122133699301368250012158017016770 +9114429395330391270070591803725103499827663830583921855069504046749255370339126443177002479229837662039928889147633055859281868 +0939693955047378469672103416236905988750384823305338476710533963327706820167318110473623797427088982971507090747733464699600155 +8097749323633489883895572126941411948413387610953411492455863784508332803867377054591645781447379503035057961144435257072818355 +9192774536491861553570480071143726445650506118482951514380572367117987602662371433431531020944047345411439555055616659122572444 +2538272673224446791161100046059076794348690288664907501420986291128393194698926073716590796828226666658940692534781574652080599 +2296719356672708630298340102476096286223198315529342168688227248665999468122934752834636542066357602969267253948955801596039200 +6907553929212877794229661239340340054507529389112502857484240836627584226292443778415389852020999685448069205241033372399261985 +6092552080660661596415004237982747843962036754844362941207857067711980933061648692291784758051313128780610506614569982941900763 +6594390851938570829005416110125258734178056235062424322731777215369780823020661620803238711218728587137816612075854867828598400 +2369696331217866566447853409278304508453286888356285386844080814982619783652943626843027594633000036437577975836948042581832383 +0948885007237584172710171695745820012612068700229335944852613395850206862454317862915465465928827325249777743488489409096560811 +5495911603990067019527969794975740716100732404562122420639048240417867412633964511077857983711654955131598842753406211465747911 +5938576990907415666730959973171116867930776870120598849297289380213982472240243585128799882672536952585668000017332908127868142 +0055033948608802263653030525631019632385167453147504582749896613855370979829552517051177850239757173815628192095505002356755838 +1575006377951552426189315697593571157901434403253801500948115475871805154047796850271348643125694619380221648554282326550822126 +5246972276283601394823321357823533983734860648197127606415601325852813899044500843947933650765212255657169653289842270182408561 +2963795283400693969427927841410518920229478373496996799858803689356286312942605835353285929732637824507016592351551689198434905 +4524116354075005325609861714463374588743076763717669989113993414649241279740839454455938573607498636902929162122468763846383424 +2179849115762842803410844590988059861418931490923531363860515702620355825744742928541840867928398829472206209785674120570623101 +1132410047750617625576007081275570045071771592714539836172004355577617397790511943990307565673693827096029990088810389666652957 +3079203667265148591706387956264742397674809071114000619637965798572137055721279282178439992636758230593462657176883252897846130 +5344900606236775949063632708480760988922321343844454792539062870953446557548709705912512202634518627191679599405320534739710674 +4862620838291477498931509821859436769537924979388766030614053473312195673928520927507333262772931790461823017698325114264269689 +0377879474949535999373402143435358728101597553950835828260875850446040578864121150552326559433858342085551900119266465442357108 +4145382952202338626169521528905963930174893641795335885386219002691641231007259917968036502863440853204735639598211642320126801 +0053580536687636415170048134719028636721663473814357492351910729926031799390889508364212752500797332425619171871273560570543315 +8050888999124989277531284920879308536986968026163965044507397321505480674446271580916011349473678101309289578265591746813517726 +9127557909914237400136577868957543371638044740008283166851799275961511059368672159330761617818453900045874047935222126146402161 +4101481561011635460925901510238481810190290058999387022386961480571796642806735909558855831574016730200955930521599776711652022 +7452149234766310409197279968867850647491580237728049077688692166936015804308350068889158066597960097627065379539367797830770392 +6879895654050303365324517468233477011666079944449521630351405739029720149442284020545119121309386081120398471736260643772075320 +5115668578180735659993042798673877885823921120201277569914715190894516855160960884928665814998466370463245773786729580361126788 +8473676321663825127148201234179810956242584805477442480418075610556509294721684997709018216961441699698479559880791844442850980 +2548233794760268816189190401306088756408880212519056730724214290704225280819394151931727397286420914387903845669497115561372625 +3358354482836700948199493625980064917823071542164808097350543961229800125334834517703864655364546055166975880935305435123088821 +4029969129548193511513014303632741396672093205336941776475040363815521979058565558783096959704099250216961508907068930707743800 +1178228259948098596059380680409565881553234195364086559565498076952409655440130991639052974945372324956190156780171629355058755 +3717169015464986627001032179838523927735142460855957466743630432390818286505306791995379349323709241837876168544133279436428880 +0351514453291951960472553858319673428002898082287147987459621016579844258976392337653325512601241626465492005157367458123795393 +9131757913676253650480410533013952331883655576543674109012844727933443442812463625615079052305206439748070329783436962037461924 +2344960044770585240104572826430912626099098553793285442582298302002106108601045902079657749391794040816491373923940544837370962 +9188063701594180602103941124399234673973525989100931802175158910488942922514429931243842291939057260146437903218286048586526737 +3095726475590315515951883606439041451987645791724176078005404488548116509110488969805623639698399122683827452715501916665020036 +3403203657780121846439527513421356719526606981096988845264774034545984754667811956286997210908649753612024363260420418331459837 +6454363503953069085653959466382547762654112282647446898596667543948487517796676159860624750118940186171824534509979950086899125 +5534857215488234005757824538224541774649888325359087056984429790126545819871211147018201409980430263764262297169187413523924493 +9520167452539794941218775260774423444492261204451489938948286717754151689428746487424118400020813014135629700319029471552190487 +7649753857278617517384047446362214242830920706961544783469693002591616096695611398984815665331683937899607935227857197308530722 +7670636468635321903676766861271780561993826318676409654225250617146887843176296360469702345236373355189175304203166464435492803 +5456942248296553684603690989598415553795292969337585462669697131930121000520692716176831920728412650435826182698878423955991084 +2896202227362420166816298936702205787111522693592087473120377589659651412246131193859654870409149102874068068052947526362484606 +6206499177988103420772046456006763763163890633653756586913836205106440292257489426046140931638490258992453397047593430476263734 +8271727426541847240045919975482895895407596972876222349935866226855009736203327632117757403576107132386631830517560425145954967 +4982163925157138555939635994371164500846077564198212229817459032427881591645290463697023178851238007473163893076170963474781460 +5340106261127351887331618946742533731988940154583287036382074049074320856289664772105865760586792346984849386692407381048942810 +8539587910802062328556477423232706345676834247983911515809748455837801552264721412722228887311117555317778442883915233779797479 +1727978384302123967727306531882414560603798977850417620003458650086712045078303090511550979287673387880893466154163854646269635 +3674694578655904749986580598395421685146833662087197374056652325798896836233778806349388223093547068287833955056912724333066803 +2847637229990901535043145484946473452323017593046078412652932709449735113175295374406245481235052704111601101677970510368326922 +2791471564967916436181421827384161108238141389611296884460017859614820709694863977807620407661790121758211278587753750388535470 +1311682212671438808292446213587823526675524926735249896308087156166254699859676499961522597030531906137578453887072208144785351 +8861987970713845651946125749765842589051394459161283498525078704659636051445265425124710568264660637581231331333166906616781773 +3433773030784699549858220921642631039795495279596839707512577767723632096893086622754554818674187555230885034907077860457028892 +0456580053379968368017945695164763479833506838952019851181545387562584359601036032261663315698690958554540494006314322814695152 +0089934480486704619783110767294727013349926542271497056296250118282426029001430520413060977864809649544418723152448749796245164 +6374218433638081986774399897692332350976246009622586461188714603950733284096247572024844313234627483374886751903785440297788166 +1964624071497448806005939877571581081852368682713329198774704389508439970356078811686024907131424792844471710419031994307710495 +0920647722499203428391025133284362175932945797352824069655533852410541721344987923656955005597638885921881748026281505225146701 +3234051414875467310335184839402728147911090604477636334326355603404083470772369446633927746336105082555084803709873030579813182 +0797776807854225896647327655002411099428092825155001834564019066984990634531216819521296497699004639732085677368728818595315930 +7917485162442064658421046487728607879599371289976088757626588362451318559683586821768061531908102225442474938214076282479553260 +3281219759953705660325535461460598775637270570011648848817233301493712654518786509665608881437564458093540417006933366539835720 +1421309961668710980725741781573712344943061720229290458895090456489735933725778071381711038599743236066437961925604705889777444 +5346018484213473348778149540609702140487404833149084935132956734625173249282732560039608351835971014828152667004296001672327334 +7640982015534543149522949001287972675835062998825649759593191032854308472369740597155188057572429051521288244481472576347709112 +9082197142126709759172581677046792938596869866773476159806997279087695007866672568094682918889951065921351285356324461616422296 +1936662836367636814393593918150500159197921407091608653457518897602784067757706017281722377124420704953813373267377728149707235 +6165532554189463034890275736530147346375384892741034329027227244542906540148716547806611470041759808947343102903725834811540237 +9921590940060069893953324679767980790622906014798953371116800849894891003273141853411016879477894602450403366892842855960933978 +7315523767557595011955152358473653019805790165086637876204849356737581570450280098237545014539966107468231674446113357850428012 +0791096311925956512226765013630421811573099687378388596324590465120594475232654312603704526616319090075205150090415360289991719 +6209585520294606187513003055352209151687938901113926217849214203137326771247519247105616300171713950372822794825746012319922943 +9070419207282434237195104895273960005868745134822958234433238365905514670266360172103212277318139495451964580465731804042546085 +0338167338157532972668177758079386489083653270128069387600686406564818006155678752863600080494279043355117187180589834925431100 +6075857929007054805987361298779916735419742025429402324367885856941288459616727309405216361897524393233989298288583563658786859 +8295608945048628864124667559446303925571884604451666719475562297491635918267804843940029802361638899118153382320671609177821037 +2582167131471095203583988388775565537112236568527071226697464466863969482609071992299656322827744545682290545625251014139439495 +6711594101773826530320065101211025715415340393938808536071030195055575879765597974028194836866850448633781263352118203830334159 +1911464166497757327229347266491166768994040887477045432967042061773419808596553435219059712816623298485611786304164577509711093 +3950559965553312195375204426888631697136007142008473612408896359608938295800221018496826509159015008898585495437881710932385623 +9874188189105976892158996129334251306053998794609627074731940358792016931428037315501950874633599120688188498035110487709165641 +4173410887164627415991784444779118017795312676797160939963018529923226506062126935003504945358174169319649592822030532548902364 +8585536437904001642599877592181317351676828771494354438414987542591562928890147892012194379909967644778402105597291718886776458 +3150820435297838229930820366686733000243384455249980345366643081949584071763731857162948259797374460060553393333323596509856155 +2746510308210106138913961915766655928022321725147254596535903741506128248245569457512888067134893515592196316478265340857967148 +6054837822399734332369250046782008884610626357811684106025662192613352002782118381922758419491552146516596733810475017566505975 +6253444244691352274570036070483313882748278795425532524034702609588633374333913295809727607310412619066512211586460777396607177 +3167935626890486882612342937895694862011472784369829386553959207681859657350791878067764914715082125859929851130735487337849909 +5973912099248621614115761320919410184939653960557823675647875630413749948258747675478750350454945866579601771984960798194388016 +9736525221109913891380740014791803647462175891768280453619965529263318350088005137408675181210618289125588737889391858187625884 +5353369021308107059796622691343010568879776199002760578714065302617216068310095438159704609241785014319825155619864292170880710 +0495180914933316707105009248960814921465681956792191416760665299694180526760284922837494706424443747428740900047510907222820027 +4261167178860446527279234457781216359164057184535569399549566971774741437839602392078186827336187328588770123788484074481402098 +3638667122645479534954458961543608733857643210245423464371257036918768143627288762652953247716922315676522443004771243444008790 +3924515546743559568892457871009003770852048769643235894176832052809028875813896140562020415910166599360845331504914716199418304 +9539930208848580218543922990732130838884473458954810662182599563214576790823089949536669626168272941913640024171496275351316441 +6014586121604715045014833563011298445249992633905973084716393755263990314978408352185626630254184110544304520462563363100889663 +1480056209827971737404379030390423810733649879094610502529510552900145205746169635070607870308190199905737823014183804945008242 +0269907341608215559877018486447313197581053179139277368261940240899846162645640750726730556726252880111678045833842135360488520 +9452683639389882051701960541518755636274003173728067144992243393027569642308110984514593826650393132965688544590683455796310124 +7380252617100440281579567534384005518334110526934315682916800234325208500512344342386934373532140277510905603770493859349394883 +1640841298979540048287075269187639921568678590871873233480612103263772909669941767642148108401983790040311768132667513845995891 +0797228841202589730512673576721011566549518209931565777081305309555092032097475454967752699515363312040210695622598933787975405 +8668051227922162902176856358741680893763627061240389531754672702747011947735221029021238076999742096989024601711374129582948446 +1940517877834907351003862153234670764674347412348064152632718218140233582465116935278604919054339813155764852687263384383689198 +1258507121750424356317899666874328759136117897322624713333775442411946490404548479399911860814088039900005338783292400154353212 +3215026566497194921100428678498475477212368831513037002482731701747625115899231609161962690257412946823057745227816040916263135 +5553936624046126710513840693074241292678003250027162357678547764315313001417556750343221645935609713903330012474781806462752509 +0087160096352464233135718030584278086059148140723626725159134239250134423251753319043440628036779710209805517160099782393064078 +9318218498563579335348341700906218700126032521018782542069807548617891695409073329049001835630809969460708500370067751685336992 +4208076385552541906663566236770990347477457147392506695455104977958316161334777343709329582406111068678972797895379263213969728 +6469519015224118923867472889765223881558607274601638396572334489644055785283085612422941306760043896876352796674858662547224493 +7581545382840053688960159322695550231110088020621219817091426507061324686155465441741816208389678694769740607167732243171488972 +4513458865704194161496940570841680853750653077515467382681747134693802486964790650970100691348806372555667204130083389487834234 +8611747863343669889389552604708894518837779002018609033906535124466043777729170830388281007572831090596749539330676454829092268 +2614980773184238784409092097541705502166871511835967598046040179529035245968990886914099394023190993075959231506769917989928255 +5078404017663690714097396036195442424905417384596337491669874839339606038090162958546298191413554764614281624619030688715839038 +2641999347004416078674706237266340276545528202969242926658631976187485588763571283416465910176657402769591804490385811120607894 +1022069138109039758566977843736614783843237277066124548095396035264485535383579339344672494958836028783883577568562124831488748 +5036220302308126851203910148295691777409108647096999242270479109438402097077523126876642469999412297820631461204592271671208573 +3765990012871705693020962224239694872611037068970786438641717866334183625110030788667653078611800346467240202316395411589819234 +3782933301188992583378339987527190128332463154672014687853873904582770103495961273158433396887983077621367280840979561172209874 +3749199929332536186794255922364823419316096604597225233951718116228041916547239168206812159635533832324993459328359641335851160 +0949078067861678051433564861906686233060038981581529250289577929864989790385982845786299381823508035058298434160670694386088772 +8589603171909191258961043702444348933790044242468360895051180975009846570524502293795875915077460542906777905339916008769533025 +4696790001285278712597004232911565273828035212555295118585512821803987256554356248161862945364958160311031123882054615472804301 +2136763839544335242427199077661794515629181187002289822787279783807001282765862055208820154228904904403489678312786030757766407 +9614871264207972090235920749815254428999808582182551807788105640461738443833466880592889986393806973211194934814261892282034345 +1053682892737737957362804191027861071649489484546703968499730625323764928679998205513025454613668271807284174919717885935584552 +0107677739834015517323287238318659939175109001778305487593186279160892671682768468572465735391214037913879126299875773890935302 +1453192783717188719965961795655288342172822178539274975297334507529484119848624649135862288188339473719284880061285948938876451 +1104549929363155396273207050870222760506597442331198500643525825130190102095717730285667006432704158844993996831311067931052472 +0828794534801481162296313627002145147495573308897739685396596733882610495059540555269595443659894160127867987587635803471525887 +9824022973580621392509031573913634949593512565298787398230994893915219176490569710003371534856099636970419816555736516423169506 +9878869772576948330900142931880528301401489012367453237909670821957956278129934157555287021598067976133621454184066239649408611 +9449658706952732276667554753119961137301412966671524990966931464264571223338509226807775956572226631814170860570478467598493299 +6650524655470467246688640367290038855618731841663664529545949374735107916410687959478366394614227095766286594581538618985047713 +6086084723895749993359399578577946854255073324766404400181960974843071723540051597517023301307855898898894953430083847202358708 +4753978848939024247917094291054930385042551054372444060172056724142258138979831515270370976027807892767076899036493276499539268 +4888375835344223898032733332908158386194226863203069893727606532739301723911436149446573440019514355993413403576684311443713663 +8296174705195869577823179373277139184472357378750746123040139737885926265459733919518384739329034183096218738593256149149459218 +8744604285228489074099091055498838353604537087151030264581202847750393201247650508352763633370859358894270796201667489608183108 +4803757550419070833057341387873914307394706611793081307683075761521436160450412298883575182283341357874191269212976379223012176 +4435149804710266828206094765772134040146772381477199506975830561360349698395986931196641173574221626045221904844792851097031410 +0140901477445628979104022134212056527853288372076077175566962012356161842830489257088195443545165486451856025672191097292162212 +0064993358872969642733510540028442337052528489493327308542431944612538748472046808937048692371405185247438306806803215040744971 +5863180250517768423942955033295423284494978569038179153339219650484893674393537942959738721683811008504606623434622402463074587 +1057835094428515977317637880823368056821605598124838652727786262421298250352722448869673429472764531868054225289565474229567344 +3983238067434669068143443350182793359210352492378691479389127933138449974060689160199725860332928925347596191014918077016053014 +9909206404814481317875154662284644158413522012241871567063512207157398398808577747347614035401411443650498361772558269985219726 +7744531085141281581884696688318667987388073192993652661160923279555067568241657394706279105517300977887168465546838484164576246 +9611748410338839797298552575152326071882814435353560145135335186607329942289657269580962404267932188572721339477733166705898691 +7946378873728294158344802516673294879896454520313415594420541327407109273815189756012820920103635963852069701484919842956064247 +4874591841062298195410390631359369161750834793590724525846541010552709961171248502344146476896644307714211419632056930049968152 +0852013636597020207693222771451991722621240083418836922565030460173149090294177983564964216327697927947088353859239269055188074 +2400549504774664967997419613371907388057256285629514767888950107220270166626488534296499500671373046737811115964349503266901011 +7846949189277918675793279134349974486913260503583373441600579959916070999842932572749268240452265295033063113981992389359199980 +1807261813286492446582855762770791231541210822077548388510880369998042947014204967053843960432445968862263380028288322991265906 +2762645433260880544005259370505499891003063278739034550726117946384596067905756389481622864019774830490639904899687312014368603 +0877598939078117695418432932115534755404639291467885726434445252435059379183430846339103652602297046524638381772417363883263654 +8359730049870608112690328069372460928467332597727838745244400830648941526818883943362511766258737811338084111308530830389543560 +2410247474136168092178004494614768123474893065682802068994067272461583528237284734448787471492174143726629957921201303786784836 +3419244084207146670255645738668183101897287707313517219103923810981141387894446864639750358126083692639837682548305250192071549 +3436195357834369331471914469743643722462664355231433520254139779344070714371148189113613561574794491575113019678653016828154505 +4439128393642435861391888228862161582205771186938296499633571218847225435482241772976029740807151825694346388408247223423817081 +4171338859609192030035030504936231948955422469935021643277709598491047020926862755743386040228384976638566676326191183360208322 +3510862362886970176404175491659583359746924395962009940383204211293131363439339555697993521569537948812781342292800263161257581 +5726969821251892647951678196842942528264097032325730950425154233756415408461741348237674984518538018983212811201588754756679009 +4778823499024627173529601893322692810338013854983738107737211008580929848242105038076828712411467725165215358148210018424891037 +1496776225022474894349628318466731097093204838845621769757160832213029122420309244726997275518899114578190401052089674878889558 +2150627813505105989559012023523240177523105542484058646465606833626398767902073791909228024464129808047176621447241995834760361 +6052017973982660323612282677894938232580284277106494794233888932365369180563831962638387127741187828046331415436706723759407894 +5180894648613337149931873741726031450761346316713499884971063033267583243155357639761188673059015634957451632776491096066497095 +7359097724129074389636668836328680896804756391700054500433769926703593431467797993796585602417512809374217324038244052246360155 +0320127339112689809171849857331267778192107524355671645529083978652864384633410979534318842451250352236642968028403645196181521 +4118883887076525329129847815663385510560303081843081502700312066224535446821931779450723308317216723461024137619749777688133771 +6860925861993428345905718490095572908986315074234835981417267582301259888782018674341030424958141289788351602467046638469818924 +0143824935249223588362256342022083873670928848969341583470612068807101099916137780686058304339569283167254333748135808339800772 +9845042645815548051061661959342234028016893229602886364367950621055704775648720419681077874002883738984453187494339471937282961 +6185757617018491828131168967624272567910874050935111978401025105754755523134728391834040088198125315748002405260114662632225537 +0001356522238346600683651153474644012974963965580249070955634679932657691126596449770936543341015260416997826513343646145044283 +4828874167962259991499503673600129164632862005208870245173240873585329607224314026307889603968799682624300985376989091014318333 +4168596789573195113960639290119776051474016854378309232971533989042184885619296187870057410925857690307120789952188315701548184 +7815816015467871136014863206025674274535612873638378381437080759117487117954420502148760971346576864484963796596882357424854163 +7278776070084196458814159988413439534481041061939764299982929635630954177993909110795537307351830954498765311432170571984412940 +4866643127910829515007652463394937334032003939291631862785106424516937511685939908344347334073225050202328398177347110978277589 +8801678473026832588217149008639268101698714830248585119117348101801713106570301483277093069752495633755950440945309944552577536 +9617526174545226424763055218268088790352736241937230497521917254797890205799826169553805927031554132395993133419267021828416925 +0588595739388380595646159745519694245103070425108579498054959496392270944865180508024017295767451162258748775694293117545535076 +8702772928743292051022384687311462236447455075709507160040356608423402093661525464261088741775321017969842598962589897299430978 +6237136476697160214138655273076686746008630927947783830802379210660809805387482347921808311217785296709410669748246476297410657 +4956721962293257951828094615247811594706401567441027963919853253912294488061757086648296894406025064932611868738979275806148213 +9236140986639524620047147846000572917224921424609362686790157846221057427556655153843449488971368941339909014048314921826442675 +5366378038534835924469785482056822213525701444985254633404597205774596447015868437597513680309327466710395924280351223623339183 +9261759487436784234076954133371789265925256106206142125748755816777807349896143234487553341181444300315188799020606387261852065 +6449231339989879656134113568400091770993505914272783860814830316499113207580889761456971111735531955653301180535329421263128180 +9593132987957417462820592468596242875901632353771559986709766159903387546786798526051530972235857175987208754226458183961092508 +7166010270941368051527588903270384190676373547534758318035208065713808112773649467017099455130795910402044915843292687098488409 +1937469715418217414858420673158652244798733546985659728265347906685952080207655697652213278741491999837300249957163250199620558 +8228542398195856592161847035652366519403982135370687548941385579105164032051119406009514327346290711302155896619001736994882959 +3637688922930586118891425546901071630399830689323382607140358997300211646909715463890915726479319114588493474688432142961034974 +3846092487931514115420051603659542019430636869539365331821500084544038227936250365288930517356505814134292254191756084661422225 +7491630641568207270697424777290522332626816338241009039782131614678232351819731901409187134000842000166738975995925878926795417 +7070750968877352578247753117115107850387026880348329157736320326010941179590211312594408978595708610843851283204781916328035469 +9579425490712849875219973084577346692279033275981940434988363957383909743141776369454560267346747976256694629456374012164182136 +5715566001611830336152118124700907641876141203182109095013901373890817946278609009427237417191831719001170380251629245333691467 +3777911831700858846402803605902436628175309291503492499557547037759922326055839219601687167716391664999501580752878380637519910 +2345655297190543682328955031766959976133728834616422943753268441547260728586436316967787919247613972239277147037959837372479832 +1264917138428370036605146743753527478105136932962495735773676134552135849746496312485051160777893753074748519890909181403083444 +1019752269689676763134331558096188560591351764633604225395078892173960817806638672836991960044167937282088675278982806967963563 +1646009447412568269213391779579827953653829284427738902923460839464242874336523939561058096952551188341759036552715805796071087 +4514073980834947668053426086988984219335479873558292410478690803080693664498437799009591988658726439726875547569087833595254435 +9375888306479510358575648650751703046541164954438171217850843228076528589259852655316343249086105052230853543970799873699798265 +0497165101239642555970983822927664186264700494353584434852254137560612395432178721562325965038387864052788810184396252265391565 +9637587742861000445296706995627451301787108835157787393998256171661400401789319408568582769562951011299994449045177879050273430 +2769638085862574311449284998834663396793439445498698446119611616703979283028314824805123683773362437290195603306996434216889157 +9746240352274295235857973544138134283682808329702423798784675201114018293748073782256043515882634590777226313897991318556273667 +4701746730536780180098002090253702648694901490740529266632381315672027202981413355374788940709298520432491519903507229198264330 +8070148149525433117389602780506057744721609297582112109576333690538110175139908485162827329907610365996630627743121137187293998 +5104791573207818901442004558359267736381147580646586301522698902268860041597652832411905302035950359208853670857974818287006889 +8341316079342584982610662915730694074266548963632208359785240542883719157226848170269734749166834340935627403531307648991671361 +0980972555646214322144321439065894684434469993258702669138171566031352957356377641038155398195278908409758982420498643103410893 +4371601001168990355540734973104369357289155288304862691393888938344400557230712070949783924178880475136899894295722616103660902 +2650480054074532640486048424756932242389804525662345983096982594522899748828272298964877383316038674083325253436014966605240140 +3856519566975050055663208955514019483009113366052382153385562181732228724721105677879336134186949728875130780075511950816508916 +8078373724394599222427487550133729616625008462045077201670397039714132392823230257154531053190372696559721189464062559107218427 +5533555275076610788085420797340154399319379967237990930040901555785674670104205199936906742420190549652702261090687000720033243 +6220111323366364001766649253557628713894497231901067623752708357435827393686138195041002675760831950319558894058730884453262921 +2672277003207147557184524907281442713352109540367339513087567600977008089227949901291846746947778742548425956666175882640922513 +1341364992092721464190832131115386139839119999152547728030982443332120739263533376607896512917871745789498861904805336640005368 +7658494769549719799407250503812834345058648333149043237321838262779633765459673785919568562329102220327192186018538985265090758 +9891469225677893167626557199385256479464450147340948798817312917938176509424351376822942297600700655298360387396846679746727135 +5585419624514115780086956162546737430419445855772977848266264876064891998070796161296726855803937113862539594644902156882096859 +9369735292641205270247208512449851304357935258980017450069350537303506725253901558161348011161752800022469320895146408450548005 +0250792460158595347750598522833094847081172052676835161342784300572879217498951077354750791561533069799550859447965317570533329 +2215924262194811929293333565918894008757945953561673129802085846175034801490539407544733104736094736753035909391436637603434211 +3003828547025999647155773214469770001753156334255534694689533941988376812539304881256890976599139258146884644045613614613975749 +6800289970404751783592624836999186310592737935219914459902710041374388930994677919752362443845898391179354567028930335795630336 +8505759963363966237797610984127231223565214326257640335015223496586184791131912377414208061741646388077399037761412502229212389 +2367769534630970786705110465175915418118488516256049569042500049966228999665875815493320452276993289094558376973109778079159470 +6740493910513290939463082366133739451516673396242163998658427933891438988618879026121788841470475041492666218138874169322768351 +0357303411110414929271994301686020384849443986791255124503432672170825267728337655437754688169388290097504447359015466216468554 +8192240142847884347063706580178899765888936207019662263339819670804657575316021484690040614562949295841542317355852873318646994 +6622263158780477778507038872483270954479088649415292887077869603719840994025710565546888311161500807362774885446004022146890733 +5176307819234962298055575706826929453118700498832781969398229886642207165799263179818071674130902753482145042794765741675062383 +2751302275326116756435072977812989216925193208517438463427139377868288067976273977283764655073728859395428187073500573181560080 +1845833119022235738677681522695682631533441089840982284202257527507809093077654733824218731845673599302802574363126193666185391 +3166900131346963127277355530406427740614776135936937104096485949875917688047955787215898240293170537866227146364747857832752126 +4669645223062976703005533676266421855057200556768903274559944244985743499257659816488004923631139517399183550005987669212203452 +6297157628559223790562907965128004350109267209905657347407682698178194211365899319173140023292544735908289515011446104226422300 +8502054721545809152280155719799579471628904200160322267901551608506906168294064129630324866997658363940456422902104004108177137 +8611455694956943077268590839116881863962149614279260195002902444128093061737269752861412765172102748566330731758569318211290162 +2118077432186424807711637317365270035214176328074110410477259086405158562676552653953649314850972984909614735542131768665131395 +9130318975369645829745860603018652270421321400871933193989890358079853776989047665775272263249474986926107253991947206182591757 +4325629926598371530112760898633445089584186304063488458578614511450976053503391266403574144006236271752461276059535144816112805 +9310962691558816998084824217608875970240851404516558899699931821439837002656912679683111747809512293159335274051955163666587739 +3902386789985655245291475443305453851903636698632422663873769701394003436882941095444088054500028643416504275287739285414766428 +6731684059779922460344447840778233714899809573570342484286950672469381242333715669289828470432847009362616600780028432025743033 +2276600041430073716292091375271727222342465787797822281415644677410001912257463959430983836963509288786612692459635796134586826 +9782224784159961526251458006646854715817922665126779115397720973464372703397941849152840323345005890646175543131277561028472366 +6526167561528291467123805366888888939086346365110568387291388627679455761130295567840390067905361907699677554800553539940686367 +5318634933920742765367288875699774586510799452591417236786364826083325845867157040527831875773026806940962750032015566117391953 +7493598975572064199793468603272146721884132503560554819191261369489899329392181449153306365084506335645635211479940033483680033 +5666857577286998209607341637144223997808492861427974015972123407497307671199006287616123632355515817115708130939568369663610192 +1205647133800994070282776117664323692713443344045889466640364601504907846992739340462456897562574412472399343745023522153250853 +3330319212199788930516095800858918371890861157149091660451404495244272634139073531985568539542425087248882661841548689414190274 +9339962909966865014181214015249254327772461905399600921043142492498857240896128334661531817157722725029302364254696461600120855 +0275915123664954327692393323944686811113159039391999178218124646929218008261416648150717574114279175738099624550954907847794987 +7571438595941848638134927452374529897917662628583906227103605014116793319790628080501185855732090732338304609688113595225239617 +9130230309512350610529542991215174872450882806867068241227281773112095423160023040208987280292016395893990306260033588936149291 +3745658203217218584383948667226718968580182712181973947156555599253392240064780457939033740602796118129681262618905670233538489 +4412418271808065273371380680584774737632471892264742468899650839254371362867805781616736426407895133489629389395846145646724319 +8489210495984684333844268191310832723383161085211929007986759567690696951893569242504292424131665261321552489038411809330750578 +6574010925786704765333558400961864682658986685222505394734228960816617397028634600968531515638569668302469579752350128387282475 +7078834237219808682337835724461339925697109440081470255808977861161896407485691000442893960515108700590859410043250399284457162 +6826005017559754965121660973424054878627050600950197534543970583792709158920574211453104472613133226055927030752116724686804441 +3714997658728942284217722016417944347609973564690519305766123183125986230197933675112901682033357194516255841107152642532141304 +1057671755594723253182271537993425618933500230542903348744403011513962182314508888904498037845069539547674219152154472098348463 +4167698979868847784276420293320039606112408368549434641654634849595716003611837153708427260322734964264330233981684490094968636 +5842205187892994596332543353231430273153910962730263311288493948447613898642287416656513718409931065308355871917064045665758537 +9483067464396684153711784947324331651250593602460374159868530726671454450164268258494437853338327466263116015842104260460184775 +9918327983152500807062535153855232542858746567492036007943220587038833257219577445237712255513226337182088357385642452970244677 +7796979178949646284046620756567408638628381588842960380640158418933607236535455721736245193350129856456748190582611676103108397 +7474868470568488770868160856619425486013084529184753412392811134951027143914884376770407337322922148422893409126222728159404831 +4025970172900887552972543571216650363430374550536726543515591505239084581125341961497432836939375430662456975979411649814396281 +1376673671354389313333179969625064091561856231653660770117134131024861451548997992193443491241771601133879416546391689721119253 +7812382563778834433247309068407407067582779720037171842393136498269365501469642413788690972515921121053433102407538535462479581 +9706575053071949026852790205126415890557321745536935615519559505631029223517371185086529237733348358256190743027438758336144576 +9006414531022472111125196202814342278375571427110180645405421603811535358205031055882679708681076022342218218061708622772715466 +7014411937404524372862470846165036293398376604107823885481595132489354330681978934995519374885800444029796119672180751145597990 +2217635875626883098820646597303117864519547801664674186453314965253480085119898512655154794357106383983041271503641895125251110 +3512252235837181461231288315033867607629725036297297118913502488196313074376766607719647753171764776345290627421411392489636355 +9367329989873888631983207743830247822211335192876356701620415814984875713241717521999471919347104942898869901044085358319426464 +3886932632919049176756333953836633255259101098478704964812325499313834184343270599669768380535271527046781188364512125103089728 +9085400532850996923140563913725057699601728324308946983801623506065312434754540522710960789580013319731801582815683477412184104 +0482120489559053393661539069411739064074531226478250805786509477678364657275374151794004014104036094940006833641747026194721664 +6223030054108461763808112515000136243498515799834948548188322786124370768805042850586372228996897524679407164853720521651414447 +2045659821087414454414430804224189873085215197748925231475200893842348103097879724089151789730563882183249354356129862099874841 +1315828776287414001746118239879779806183081196189658013775774856212674965726153082362347461854112895332344854176341904165597409 +1213515137476784596057537684608135941219538557595828609988436459029859804112595309586038750020095414785054256852539919216535980 +4484140865975650257767631970278524142097436320367062170812973468859967284286099661751054315067110058820945519136720185944895374 +7742692644036659798892433853855040190699363942257261289132684570773772398269153067661790938476854192452194252168071064023197248 +0852041937859186893599452942342756186406516322350714595377388609314520833365297395472603829432190992959559426685189491282347057 +5433185143289951693194250541689460837628217616946850778491455755537650206858581434235195035172795441448516354831307931147174885 +0450942348007827481803050062279398301950579282155758680606572572109484512358059858330484516434220157350961537316843651345720503 +4380803663067690067136823238680695720389449212682650668097530448778728543739620300359368766106338322729357455486494754904098461 +3264642960305581246967575470210497486665490038206044077023048630121578302316138939937893860519106763145890200076403810118822241 +7107639168089983991587862527040150737165433796689810859573435301169366192576251898692883826389304101824844158348543802249127582 +0669598166832510431109943775944521732861864105283325341271770704933734103372804558821648326293865298432780241079718755089452707 +3568107971104085428061715527742724933050823192969880425640410593395489624255402189258495659521212800172822796344882503818407485 +4194972547530169348356862971069263077075953903800152306693089832494384056021498593498576727061441507427007792270872575143948044 +1172788991936383893646302947517752222941032857029795292428946787677455477081292410506859204482638074930525197495818984377516119 +8708727404823062734669585099177621030117278743659596151369099005394128728746425149879168020045121622056893025422397438542352826 +5536557575335193916360622332260209029723091397745281648820143235533928946294383911963312581783465260511469849131842805230470062 +9799128949323644060685917638538635211442541566486657515013727896653746677089071768007544683389875228688476415065723679786222857 +5615573863935839938823973115950563644124889855629743447770458847381603667537087961954383608440966294356327727154342428403717584 +6135719365304945156195903748702135001035781887797234160617684449358873860897697192990886253607384422197173334101390229305548708 +7355386834242660296627412137907269283682869702323709426719706244145140528071269497974044139621935732608883353990706800769609401 +4117824188478783409286690916041542923147052848706784080198351893505523979781915688089182588499549735693880215954821551279637108 +4670168752285459430877011145545186817108898834243886734027847064654587388165854257852169040893626949216503073312235415138548138 +8311486748387427946913806861679281620866414231742557923760474181682196182819449180979395793050862114181314437692082443568986323 +8048322367949325657177291617359572925594555792044926031542356556453504106573058251613848469630469333674263842694166478603019606 +2241419420107086119951172130953831261301246015464350985640311185360710868324911647982596380951605647282292847958812174415734380 +0207875370509633314857109554449234589264638078127939118196136645009058962335018366740029205503954796978369681506112588271652726 +5224125202089733016629542238144949897132285324338195131993745529968240465385054246763922445031407439625787648928055552616882109 +2793536617404132317781661381899391556171029177272445009604125493001682989300317084296686416290208482553512764370697922889547543 +8188433691033017668731418898412943576673580084814405684350547059200277266867531820522833168888834691511550304066275553413432816 +6069781060072723977215798057681530374205235352090719032425665651313169145801463663003622748057443073337332287692516062915866474 +0991144941963456299854456491439712458220412985735998277280486665724362417204461793739331274746630407706323572187551658131522029 +1128985384630705817449509424373236680198643102398192723995214101875334517122923435752591437827690137749416921444994412824571229 +0933149859869844034267514737213635247382994476023098139584524288805316943149032915781109345152673212416893459588048333327424649 +8431499800476711779747358632413776057189971388402676768615975922425552726845845057116844980552680355154340958763735731899549226 +8999746969039035522234132566990886859367771300359825363759150761208120895977530444860283076544729935781975800213516901282765869 +8578384564148446021966192696855640744733543364607326875346561423047230035981121889907856300901288650704791837601613641976512860 +4782737185453902550739147496508870329447551421595794641391271174654998339963075546317914475664543351090858707240517793471880344 +0532832780888888968522715514533732512924561999915854959906252640056974250958067417634581049729155174966017949852499049433553005 +1013570953208696598838952288133824762193269724513979589866416713036164401176767184475902330276700096926543877198993766233942302 +1558731482683686439823710758826362624215320659618968974586082717778992330445630506757688512508423035162517304559268454636939287 +0350643207571320038261933704850349754594287810435189119965241800581118358691680807766625340717143030794170306528880710869836401 +7542021244067320014248296046012617744009265286583021536650506535157160764351137973007620114482584140630719966828687210278471738 +3972594767241483050839240086921327315906881022442251582254034730627668558044239515436332898375100074941146541911588915186647677 +0697122022586499143860386882839929813751234590790881003673047769341255221587898768035294711863803910391010620206350075218304635 +0915512582808181894140392610882201933416172227163788895549745243551406730258634514282881847090763083210060265428731131498145196 +1501512625884967191270103224021116637844888067197984181082849059840793925774486252348044423174920237439188586936474269289045183 +9546757728181045784907692074408445085975750656689918729547020741600264958315888287557582915125013916814828453001978125724956413 +3762013269830490722798916645093224443451891592652341999436840113601558507630551284115296518475712532341410529593546552381421688 +0535075660768315265313017230395001783344059304089071262026544062275821507449160749765605530180854702975356649517426392492153632 +4462229922158652059017569592409947967925255644154395071833612619360534506981545280294824268624971434641426791536487863590137885 +4122860079882835156235088149203967906043976190683635109983922591971046404170885166646247578979128875141127938069850765269247014 +1129866849265718274907637855113066201183196338524085201233097892015346569822121339489136356302467977157281536251430774161642528 +4989929495806193845801738597980701228751796772240750395477795189154079611747433960930484756810247462060407330702869333431213592 +2584408285550889731205066850550890247663800179003073740079409662955268984464235081016863796902152306425169108972643255331837648 +1846495490902027499953838660575831876113795963871581292212984733841932116361051031722680807069640515568611052917719380714508500 +4730368154761060853591867016650935034777443474265562879400822197268954342492544791524055284219250660710445080227842874954141063 +2999165237216316240588784731941187402326266210523501276042349025855852110771327522344504598981978041650966736224822421754899433 +2237345937661408885897695793709671176319094949111986436639240560063478917538944076082940836147591761497957719533329126550091001 +7598999755791735199200904311392506066249434460145555306422386713523721542555336766166589674039359217816140532997872100172694264 +6085462229902194135463330821695657542854346332891821785703935541518889566865952336969577854576497261635473651422862788436766766 +9330926718781006335569837395334762725138935227106710853342363931868717426383038886351253414292607302835343171708502682390162568 +2759725885383603903963134803626246023341697535265756560586925977858745569648871684971845885034365173610581864076735566479105747 +1316975221277998480665738247654385735782940591833339656446094875845628844928541054371377477189441032462815404076598890156931227 +5084047857098057061549088214576495134500797236087647217879151791731034614882352668503598335757353547259171118367223224526235321 +0797097525215466425082977546510079918770199122108187284931098479825643355771575695825936128687263528517399780086601002282738464 +5882108977367740469432474363253310136350409797015594231680654761034810033039280285322415385168502267113255563957206593967018787 +3765499022319437900072126982943230712423709856742749800481145115762174065774560039072882469409389283337021169962653600686979665 +4601581997618072107980461792417647044978305789885381659804666757198975786036139854382381412304035407153678163339829269303413079 +4804720639771780250811522331538529037693400650190090078194406321327819886301852415216493527923345326577327202798642624789731981 +1262413264233591997549583133381940611046336903584987787247990058376502498817637376736904140547566821566316367905947287384644751 +5776817509537024183629053964111738625804577685331848301319393405578215972673272833105126341165662081776587402862223607527237841 +3028857137505052452119497280084550108942293030946862851209526606327291674235480503744471700882929025765105260116471991605614081 +9340100683840148792620022922546803909003134351687693370396127613819896251128556322124687119185535407506368947843781106277912265 +5837860085277971472343245356960717599185735265281223333386501908378356024658012834775741482445665420888316355875215276412206400 +1493889817668204302273257693995210600140074382228307911629842034054862116792315868260039297702147104111933826341459504324514560 +4753667010660505615221578575743422389046538837003732162433830910088895626144829160559920876420293005201234062697339903600869729 +6354021869352141987691776069755076073591529011311969753213483772524805097648788519425735403544963778868397356812064242294599252 +3007002284988957442402223796792807680185699679494949671140500739304141518373349678385232543712822588498429151363366914067465545 +8768505870421737992314357883218463757749966400771061631074878673057982116800200818694276568644501192730861238043718483873582182 +2517111306024948547742611078204983908881805478217286398022649611447119014425201196572509914583877236045184752832586045682283149 +1376425079937583047628837215348969478849396716756967260659465804416943561566523910593617918317955498182529845524570831173841723 +1739036166752846450279902298627287438597800774942709861167238037011909085160508767640035085875297386568223167138576660874567993 +2749444684052418908020211162253183694144723421354516311236418763218072849726536005435788876083510665834462678112083821685346156 +4979778261313034734593447279308534789267778320343662336147199781647935359075816167778411424508493052174895307203375091235720947 +7206632108909493796814807157234843620292819404196257674170712616419318834035027854762640349933755746419728237290445935893359476 +7972523340752476063451026561953128800722191374241698295085331249527419993680680244353440653002242129183827478637542127263140061 +6106361246560664390746249407209413785337414934577733325155578012914242942249188259728108681483189855141297703781608717770284437 +2459983080689617774602481236618573771655193699359105459132425259318658958293509205828433466503731104595644550263940782605577596 +7182234637562052669040625972588934688697821675314146092726738283785184154736418695256280074985786382718815935852352072100348316 +6798709996334119952629721938215622401513286500076236957073743607147857811179119661766817565585334251822559283874472847953361475 +8077368484549177973605286548826253514295310845993496549862916394171225254015547467894554807332772219846678815481086218479850253 +1557723569466563274008492657223419903746768944164337126986375831639044695750746490501676088871281694494821017358352031765251392 +3419994305802489285895038983304368890483549511884971605938842543322203428568381657097437964153561863023468226697072309642108698 +3155924019915150179568158859379848450337436841515789553391587979303261228659176045375192003213895824215877498665997186076059865 +2999137472267175701076913261675494050812219217526275916261893447733447516002165559492227300890776947227359067981526656072673696 +8601098495166116027734424221556221472614130194797807299983947972112789211510616421380969053402247170620773625487954585201101683 +3246912051329707460282887222043348831157339566467828142441386949856139091307353393247884498427787480035549002968948796358423112 +1150576199110861158548115562719716941558388002376600504843363323295796185489468606412792484061811432762347078760084900402466948 +4752997880608034066503743367113742314926769742881109939696550927489876774818149369014784149716022391683583701555271412269098049 +4122947315189371954201913791790379829165934721428116210038908501041491200253464990161405881080768468552171131345401309484164251 +2900730978305956981781132350165898118101889333020350831669690402919195278513062935410969054110767363793161548411520509850489790 +6317140142444713262780353377445709754525526526100220931420853524029521161445026020859823577131205174501439000115119816705181410 +4499991523210057790779337069784661258008182657881453125037535965670105347302421579889956188914279278573529309721593121892469205 +5508146256593369763940336135259800659809619929794178470062048041370071430831208645554427627646035391559292722312680898801625334 +1648967526716449029810215023304697906807519889979105852637730516484206305531886474058371893246706713362439898050285759339179316 +3759482577410685684593596493926547017312369150876917333521714609230709447558843397643743851645851153700140054595127571231949538 +3562024855350127743440468170916592187245116700044293261821848456699166330596624128415577062851344089770503995251522564366175797 +8477766635255068732724822778949890633417123111391309938080502220545229819981424955190249946666498348138015162432180329219887241 +8326147305998052694691664218432203744648063166741259109591553998325744681167769329433557902221338261010505326465825260282053606 +1089081066369978288230079958586716934294819087031912657105656191281125863299636207713663767265987214971817617524167760055475985 +5956659161052521449892118359418646820302358805073751690270525811449427458601574354728795417080178749838253936083489079736869589 +4756040554221482987126925729358987605986625930694194674956898248814661136244491613548633391179640626903451535330197156879634503 +5269657254348772763363513450959859105237330221730828709908803147787150002260299247932869706456162552333441429917744946610133738 +5165094570822740809419687589443353580562603424278039485215120717937567901150913213688361129548604982677760158365547321585786532 +5721986004191270987440236388972839714531938869537328769699467486823118683325371541953906389947764104960444262942308606574783420 +0825423274999976416479179163665065444342056794814403615470874263539258759123358841581177125724615901000777617590704389294753780 +6742419699812742131364238655136167729098672287631443854160975671169256136755724718145831652290376122821890697634805848496276496 +3900848450956356316829187800619138955746586281240305331127062820418337136267131988611645101253214320267600642044789378308027813 +7768142101816225544376840102775693516634889161775441232950103285109025549587962218777853283361116377890977393111956211092720773 +8166062372469258701826599477785259752674604446062893635372304334593302933414637458058589360745838888558751703330971096452276811 +2564850420537564339701047323195837094030546459418840734199428562634836997313660393990033302759129375346102025176996197579767609 +8872870135828449673289983810960029335233706075121561262257287764860415827304140209641297619500143080704997500176861691643727930 +3523914550868909381989245660265536145050317774721868915147125702074806836551953216452075481799528663908105214114354861340552001 +1593559589951778598427586959614078571783195940556392469576085221648989891950185568932203712798348499019366215767685195049047391 +3677450698912939130282894253043150590674429032380702463421683822229640128060377145549638924052619130706857319423374607432012118 +3201722283329551818782298921051193764095244804825793425784091160773607662095205922340014721050275267634431286245410339294369403 +2407761928636596422414810603411396624762992445889584536298033386878386163466235099237586521069756385945437909851885956052776101 +4551892967264629771755662186507262378960508623998075765778878560332855343021946501189553259398537769291592769103084773248272732 +9106997275950792264693136508466046590049216555344492329753333502148103978681698340982507624277442543719542272924397318970160593 +5003643337652703728951291818235842005801228735393820041591928726474748137284662169580644065969190131576241311478203049648965040 +8986752392715741495979561841249049222033021019539768600866686767034142327899022898431650915325022449300084738502689817549514938 +9360763437700859982761849014720209991738148978736994931574590369983794309597034958260372656270656787930070334337332515206838552 +4451175037964641122624713478708404279076794770827862704532161639277525181767823672699083856308324336587319988741595847500500923 +3042463864865181170367979354550819973787820266099597727415684485159423697903917044701020373206808416942704276522208882269559354 +3048080200899219985092787175219925954199186801590258882778436766885446862979938621696230463254471930200991285622531815432909506 +4494465194385245878424876179842789271630946260267264275467756829223006003558035998437034034758226419782837401641627699056717218 +4254906820448949384513753027180628971432660348348526219339979967375196656324459448972917993637121707012531419527163028051764969 +5795056709264059459045347065077313003930925374715251843125468051992798115117609874425243624614071216341941951333443769594712905 +9786422961702175763231201440570153261798473160575498751716552119467779736203318520988291431980397558340560735449110931384906041 +4425505790631864513694023511858116075339628625002969403915799344384754821079679161200533710912237340094421570796695940926114650 +7558163384552078162151051048460153091046870543482529259775071328325205703384361917997334121827387863984861026567298429845774499 +7024151222382954834390823993921385978213128615164507102313528814208567079688499912070171914927156859122712993045849060653099070 +9429307166946990362134229731057708239430020956417601592711428461279614801214510176796114831132813782448589275144006367262952407 +6718941975166588281116323921210724119843734085559061091993083658912394337924185488399990441930203011310984521684045028699686488 +1289068200800732284238439423123562064066343303384293220311845805205731603115472146721163687728630207002750751276592100892794448 +5346713601139175262412572458619231213504622828349869740030359059521925593845499598173510818770588358689016443776827808183107359 +0509643523301353915236067697449265692352443278187907371809463832094767055096167823904552345535023947977150405485951950799761287 +0296312146861071422545805752124301678869184550494728174693762162879350214577985621448093757895707899661121270041521839117379026 +8409726864010205799722565483699810469720898682668219634727965108320651761711145638969114309423068089263623838477654883333920986 +0701982653061769206880551891091640218809670563459796391962611157072335109465122237619770084725609181414534082646884976727487576 +3526106930335563839568672131933814040405238902124634964052890265269753914225841472646860370300002991439661062411618062909298184 +5489284594499017553239845981638823575689673477128445661568350850251951827238826810782941625211567718831490113495662231409070347 +0761858518186015339779421490664354210470217380607247145985104277374945800693689645130689632481933731656998223732805590077884069 +9539065740290512427091282228814664586680546652097563995149483899551962642839270109575078586841792310434801955703204357001468021 +8203312934682104375075307914019684558657139598128081066927393125488596174624606696359883862043330061635166484866461998328167370 +0125977272761404461319903866680344397363067561278546743044165247263103505646656505183679223334371532724158953424891372755170794 +6917119468055808943262561786048361427010843965462318965409483983665658166958859076133376429071237660115286920877219457193653148 +2210582961962854397595552344204221218872408837841098089244290975638461916790216426889841183579823514753626176299268639366257142 +6938938279494808076759108059141376006848614180404742864748925105751439953802762622388768762467629341123548099684486115445621424 +8751508911757378238494838354486964489105407120238937324611137021997266296685725619021335556120419627792798600024754767811910130 +8942964732151281745754352094851080075271176779020939272053185795856508342400932987956154784656391491859464794187568016580758549 +7093803490558017110343856896961686184727738060163871518042243290379067462803909424058805017311424240997471204900355661374314140 +1566736119124005628798032033275204642554171747963659618881718232992872787295516246973812659316873285663747257448232242034285446 +6562507237663679024109994426672469847713139656813945226527911495634070212834050520202668179078431355382392103665110926728808163 +7163337093376914471075674332970801236470473885515009415164064219689152465011362048740380213961234696943727976782304542628115287 +3743855222420028553131853842508021542825305338712873483864679766747424043976484573734764155416545908654414383080932700776375519 +7834242892295013548785979422302177591758705851513681511359120504187140172485698646885346881713211672798953029529425679446990175 +4633893183134377338289855331963450809211760110920084232081216223646405738494934518180959043407239414117561447413964700328518217 +5433662660787097930154056128793373805525457327147412372094936236754711564146018693671222546348461125048100620268398154557791577 +5116486674264823893675387799935762509658389400995246436075653037631158518006706329179420693907432583221559739906432889568752110 +1646689708083793966601961846963380825939768409816872372745312253381435289673316700861369097472435861788080748455563750632269991 +2394913678196583770021969391869849166343366172108313828891776494450171319520427070741615104344197304147604907461161478146821535 +6970220265997137270733854244424832298730380713169130932100655564124041223879414671509531965328454187192449944156019761861229509 +9043786409994994408835814038392730289274231805044666470305971162880140578631950679277309655392115372566040718447348800220519568 +0260472202194716089247619131312603168906540268723365507831041804698080034582459501938467511494620080710391206340388730610940838 +7326118518322238969330571001468796819573761190336351214992854388628577544497438052708919701742914513344327189122670316147267946 +6390506784888882547755768957113718192319906860888999507544967824165514074944084153555022322181782516106164395236293833936377054 +5714641021392941796626276072711028884736425391454865594302431453743809683815572479844749782164523410744380979378894640742103628 +6367297859579910614284261237130263461592389581599593438725404642412171244089574507753692124870352648825973497683300706972608091 +6528879643690843406788232137812536357500339309765626622435903074816275044864634059559694905228929605192781404349845940406623980 +6885914653705063359510874305420852766939519431522048421204491529430787666838710231728773489324489696476746687650429532899868255 +4233440617969212981294448839630057962261459110072028596849512767604956758310477460111261843845740348872559542671712436710774055 +1508618282454022497140169566835479909506224026572100667557407598641042136865104440157420255694196211078208396396117860451776365 +1507239834904105526832362883413042406464314583874137343256081467177503191240584039921334732134114272817444512404536172921303844 +1462051089737008115697275867129401567569051007638125298802754342170903145796642346081652330045536281219892265708368809837219879 +0453685864744973901237027525243434738854537803039815016737120599127474654581496567442710142699846446910049480896669404203340348 +9346689990227089014642019407223078337725269644715473933462904642091284492249290308972584444368741981346317066840314608857364424 +3692842182371137943388889815198380579665396475153546930512504814795832691928659417816231253704717088404598973194431677726441667 +2526815319204901006066476270701860537782781626529137506566276977867530476935375522834441559090476111355166836264357254224488482 +7009126058180938597916312502300245038383750490075362057823728327394777642420436778059817272275806207554818391187127829756177915 +5110816584935888048243109800031765306763187568267294316288420157299418121619714954580527036028226556975389796598626288986624596 +4019814997533165299992337612192568036796309043358811887855691200355299547444919142751489527995336846485123932685995757878474429 +6005599839728025790953582536623365308961400544193243119538823245141014368431208016452105852057876883877763572171550179791872562 +2500356597670124887859667877622171934055903916486360738831160634956448169247221568037938498171280531058898708454833291828495747 +3109569215216409575065435532987897643346725582420896049058159554867798859087403090846590114662541568279219050551516019161353868 +6185483127396494726368946515385004646852626975268477683334752638226219119476553266373574505398051454753329418244962919295715945 +5344724458607009080560091971792387153222793929070066348570583467465266923727598378490443838259560132593811322661974372369019403 +8458407692595863178782843772482279508788218196221474525584418608306184633515880367399229446502766746947115360332883675953446527 +3121980707768424272697486172479450164639268792099956499632892432254994295106673987928794494946432347077043928557939886000171261 +0577050269924549664323393933836052936324274196559262739026908246179444460993650264121746529145829029909925451248158087279201429 +1182711132849414035693613289891027291528838438771406387710865605582990490514444755922132927296236610194750925496351082081251878 +2109102647103389955876851996958955042517697886815050668367645651505615553003883543425993431580868653965842109551601532047774323 +0249085151080461985544466651926919056622702235431604488689367549402288274733008707563604434799848038664792491456040962095929365 +7278999875589390549123015596410656975040860435505974009899756364318118677742869501641184521884257988494124371969275324199080350 +2143525889485226056728303512644071737922673006177865168448238248795782597126416756968158704999395137061079007456174899879867776 +5408947704672811574519430929617541968994408425732774912564950806181733784295540418756875998713610000715279835936333399272549796 +0046618750251923685648324487668060245166359869535034556031423916249395766390348943983259976779662337330101459084587467420377611 +6066406518226332392745642377882173038406100184159399300600456331777383902825125059202230966872433187523955621780460854740743440 +8412271363530715396852912523334867927661153444730211881314493834485030033457029175684264387115709617680689679312093978999879040 +9657869183165570826073481937962858496613310115706782423102371799566041214554100144472536538085009196821977429400245149738069576 +0816241320499554734167424446072196247447636657341202004109886635882777290094614656786148313850718338218604613751038747577375251 +9117319836426478794488912868694112364734778843674655117850629082666869083673489067903986091210347641126774671760575626609401765 +1008118532396406153376026525242599592034858556342525818569312902010080131834952260460690347206525525021427489641699256115948329 +9403478796471636735520877378951136546880804020374976748551718084257630575582778549740221982719791953968237118454633169542490322 +1286802823239946142833722240859834876694232965252258418975725767245437772637447581851118844815831626565504328510567619469003430 +8292609187732000353612944093829468343729520826414837631688769850723905562968822078612443055462285102551209743540366646373935564 +6195307285792520347832027057354006551256656817339152300843201008404344101735346974655890347210090580920518304844457209111402489 +6224596631460367862650134161563558944936031773882670339156933695467888462978309028912902276648283451994600576418116774756356786 +5472438323418087607827579818345851863545677098783823512135936630219825008658803838212318120192887213931109375285743757983871070 +3462295993623789324823077363153428519235539388072977171154971504655245868325675529422838022778549930317329461099449011610221962 +5200838592621585186795894021330350557903923860725207306860907835414568816984390887997385942459848064357524983387010427408109850 +3373256185275744974070989354450321389766066372305525693275351316959271980719451196548752519719437550201627682282540860722912254 +5801613469773755028551251430496692677302091408721986707132740326731209806971448849117149983546378567141073671063743582525928527 +3697803453226110613863663691507092125649804560354539788548901908917360870034087584677937779281144785927759366742473292064354073 +9963717672138768910209602639579145502161357030053667109664830302862052329208650435316813897369377235565986275115675225384254363 +6761344873607397157287860030945733938977023836366529156220217899463330627464716487783276767734588772780285540219618726811099276 +2472361254288850026102274317845905290678407546967989656293686796378605109224367092391377532276351514638630925654015471910946545 +6893284960359512924679289275759756168128756913637757439682764283483027618435659261867805209746618113123871290501000298928987223 +4604230631532001258913408929361874112508575772563457725381109205712576762498230243782724863115387239822706836827854691589225919 +6353180226120522335706585585655463329450214403091956830465201717296254555917557791034667593584676865134148740185250129625269812 +7681527901091326113048786722462348325784769942754780702258059604458735745233803955056030921585788629283445553041509260093245352 +7309220586489676352977698261496440058970947266199089765873965038198042811288461602506269407874212537834374031596895817266480252 +5671380781713371554805278200673327258182545730441250958579106569242653773093761570049297046722445961242199585137608405012572007 +7828486850550976212754252064205756489990487555478160661135559387233240543781534504267534308047400112433806739577979094229507603 +1735652617764383585639984486995149658650155492118012228612717608699086389981358293860866276067465797190676060353147557719908880 +5809608466540093565672420660893504333126497209645992122475383330847359820073574627113129012380682929130905963299348891171922548 +5002869804918377770158242860178234038771989441187910348213084777623351555039769099812484337067510542127104020429900993982081671 +7709207109088318832932845793851072838104821066587277352066221192004565750065851231562111055731251503571280787745188052871173891 +1300675988020851714469995143129598445432183378365444739669948446307812357058373524561004834384315759687175675721550162755500065 +2986343436898030807360652672895860427883496016032784149587776588259863806298337541233639409877580883769111783951961729854269967 +1601823981755766290542611869833989750039385604668769648848238807861573337182702195579127745245081404264493592362053024115769418 +4107102716492675980158632893016973041072334043127798725365792088221597132425309628489541173280546793641323966773146909247868755 +3819683658930608253697914654878640066261793324512623727802178114862630064529284630973855427311675132424529284212748792990550668 +0736093271588205855246000014782826243591602482151161574269309829561758900770381929089531747951679154943426373942743857678907201 +2579391599623221851200754933465381040789651168701188912204464482208095449667962715138795642341183881460864419304811104815352329 +7570162647614550188694961496075537449438462429576197619766568228578156257291119319362788331980002153009584790725962518357588642 +2867301400919047583578998934993306274019943104502333712247027666654628326692485228040996686989047207277332133939435024962426111 +2800118335388070264161106773949228910777957160444174345990380796529948251126974186339060998840435346432168006747571320370538154 +4476893884627071583698402476874552862205711529686822374264860104226019597554331616506435529408791452032863624032022777509172662 +2387360521387922188586138212711962601419570169405135474539424296757638201501368935895796012267512214174579198583111876438042196 +3207944223864976640314026742675529342045001292563111065130367748865114709153385711357232421423093197866403543774881366812947410 +3024015733350974607130082705448223007580421749047845265584744661269361681112069541859763264352543531462633691003101294808859592 +1169692162436061367720119759930066692678060291627161481443668541634202666603165593913992477700731194838349007676531720171279849 +2751165267818731079924541076614810221037738242037928099580658966017244137214097278777683446450716576425458106456674343770827281 +0396856995901845858069688500739423611458585131275613789773338642449531843526049135797567793850609374983112677866381777610197584 +1531087534395262206112602286543697479767208631357778942636385138053327121554947922895001619615466561594121066079313165604153569 +2093692637556395792176696765614996657813671032355821221008345920651772816571893227375691474134293122057079026746734194976675858 +6656377492316720744820927448196522955924434793647944766092359157144753759050894426200065451501628415477521405487862719508590906 +8342026934212011048128231645702815266555959471653323962526192450377377432253217447538860983839942343996469379287531744331673799 +6119650763732836889688973448048471041648065382987440098529987344461076331072134069257926150018448979279801859696456939951283072 +3364756734455969829310248048507921203362218977909063710162344883654504743601191459570909322659994691353369156210663856657262211 +1878353636597017372001364702577584897453891661198546393569651046602221858825228640216854117340540870937305588763063713204215566 +7246416818039080901692045886943289879346943533530756863194707455582283075691686089441132581902594229725501704594144338085541945 +8508829844172609725721048474190455937918008659794137613216237230265268215971845365542440688734702896182248548131537748110574617 +9749837159931616345230389269791318525672049015765330922830030005687071010042562156668767526231766645728311230400611546831340883 +1042438692813477410787838927504883371438184111986343392632454033632895691282059130126453347410816312739668264804223203706756562 +6361009275683200524885847104957823290376975509915625274519396770442677593644815139040216252662268629183385844801360269251220612 +2229763646114181877287927118379174699532585850694835097303121614336315830870465640665166803477820913104310061050902102789844598 +0133060603914382154852269620976632642294770629603193082836985616373167078617561223317446714275063351779922957184196466970884767 +5219496051735919324804049698971068904312201333312370591154056704186542719979874616042547422134608890139798422893654424476506469 +5309445174646633737921041425428020908437429638540244462052129439971214228921839562115695079069371301404828066341916732725724068 +1929599109824380789046935363381838679112048207357698712063450869013602851026654755608800829647182811384855047709407638244169874 +8711156655539521674331213489806723157012020673218852893220562275184063123762927470825532953422135931696172529627131694897868313 +9146092529763359061139625016856546857269473202244951373230403084240273596615596144680054956193346943461881907308435567570589732 +4427806006379233157431109769793014446131865605460452718401487086503103098553754737499082181376863362004474070049162679495304786 +9908319411586467759323529251895892937674894302534759585448543523471481019079407250526711177578259561097423457479183321229451626 +1131371780154610377491766557592293428206914537775417828678921143427727968850975607814254333398412178045797967112746828184963972 +7463947060005178881443883529993444944563705484864960478232839522887114302507061077267547873033859704180182478601068253214443547 +6363654733262110562720880713693267188885990129857236073837004378034866347649760844198517835526780984480422568814264124396245788 +7422184144089713717140409869475766686474514248637954570906655127051840364408955207306296810118720773449322716315645914875117890 +0847277505961582786741295462154437342373825998662718119420520873688904858163025326891259732777158377960920534957757187301680750 +7127332835591365255818677928303920688285753363566564622227243735455708574607542891965353908456436608550001451776173990862992161 +3829054399301597187964270168808949938169540225900414534726047012413120885338161369990845308813286961059592718025627205221770817 +8092148297686870086401035932812080011392424084497972356658952923082172309746025699168011915919281009722984056602687718337362402 +3027398748877048907651150695381083835937725004446873842854430149618594741819924214123640276762016797602129360641044263480412772 +3710143070992584709931252199044234825397803955966283897640299886246679348943101389030142066287790993761176511866201467864299707 +8525777612726675246376342834401281272453399310074719106459898269090225065025922311415205451670630902470074176854745326883762644 +8262544278833799829033118364917993352082125138925496717970983928091600634758984244372250880458595883955890831596406628913521186 +6897853799297275638235876552327431216883341950745982036695977282177864189708565950522537254131161076036929940837531981536512451 +3213748658129585636531039879081929302798794268346651462737129452506002968774313717689240477017446831448952135158407313634350442 +2055874845167569647361541785365745319937172123917988776868257061731415878495755011208403654576322652419771761741735204486134128 +1873586980474608471307789365974467503495691071252149719435658541818854563735190343177059527503193288117651496365565656653307241 +4168177432498780914163093904219938645313613562804544457122557236850377399062589146156364240794704626265205228184552480787389528 +6002548119488363978641919486071068284491204688921592270835983882131004647117906646204653024128798598225163921424999116659512574 +2157407327085931030684434640191280950835140906873052295703366686157391470840100604628128222870118102675339834080247719766902753 +9516075696286688385617312294770633537786940805769023218236327039881513937432194699645278901401806327344576080861704678059497048 +6871189362548323056223946109945917378776965980493422695153032621053464957232674797346887230723234348508681395275248458966927875 +9336780663866116286377027773514857490216582712388958421219481975628619943979311778012113187699153459212550397452631591800634557 +1884225357321516913996913776484184458840279457439252830670922733976854064570789902403772321837879511792814698370553104269506613 +4608342183802630938543364752477985342180204673563980704638103829760160093636516325877632052705116775395863314477370432360214625 +3293602222611238360052445923968119215574068058661794694301159396049393023879006675439325347420522015724464971518661959121953285 +8043528468318108109054413534510021714964009352370382732039845180887830146914560592175998791412497342760924247174793888155615218 +9953136600515251517270114445026178472065320103271749951560132315486968402879259134348096557033081980214662985678737805295776016 +2082425869735558279582276249486763021868424618618158613101945224876744522204200404218768513612549469324132911065247448884834161 +2715300790048270780472694746992029478410340286154036701148546125752257415749783382221561686531788228065371707859970836437758369 +6586934570724918966767618089617174929582836792685611937969070528242452974277966964414407164887044841543253218406757856518905749 +6701113102105341541135759678684819193668388121785636742894877294681466156143067331027856246686661690573758062813040020779144644 +8573371736029851148801112116958518768083768805313811181481300256743484459392633904102235738743377368774016975057841718194084994 +0319902874585606893329741100525933056084182554392753427931707044407179198386674603799910970919558297811591565044725137884178871 +3211448717893811033719443147078575334620516368575812396883003880411447512133976398395409888756416803178312866623025053382610669 +4561353382089346913344316837392847914820218419836561651969690056195804908190155708078402986544580149409461279682788447362287221 +6541324108089851136771616890125941444979071358059045831564141967544312392874032082755961068514362364886944531733042486972382351 +1947537457345366294923388536623305727166280503481566200274732091551547741549840732121496504900782994752246247415294786133670139 +2790662270964346469038082957923935596601670431239525874522807293008957620170868585946546458722568195100218646703049798761457890 +7207291855021167963306163201383620528292099344156171536132565162121580753163039591601562538194503052311054883261440201088776719 +7081508994123346370534091387062163390235318525916429277771533969354167555829004274478142845946719915110847480617275074050516329 +3008933084371763781228021375479207498806120584984898188493731541257384657224732160257247009445015469067201744844463068899016846 +3400853085115213814503196501389761175483359326430453738990669485692964833263969136641748087708764716019323095274097377414065683 +3262060508094820191682139681063256834961825192399541866597766579614721818676700322392895466758698832928065782064894903472978312 +9760920512587884215163543988536526156574865982745337953096380337326849623081010538502752692990846114093305595114511445577198415 +4534258782640418619628264506406504403447005968106774436451122846867047076694261073542029684272737494317052196031968617018760037 +0005723042928179923030772189645554654702738582913437716383318676665447081003973531059059732707456286646505439805283337167567513 +6867755898727425848664558154624878031288945357600827739272457697556698844756274091214882872082839937589030308224565293706216309 +3727537228724168866963116807318580861851195574160478712150379828696545006021644250770529425526279645439265900826286791538135930 +3153997389799559939607422587882124977791743840140703583207584629133167628782910497529520542920062933572958127405711490497737856 +9067474435390875739488758031979945416769209293461704806750415570467527973320692921500611307072408238071671039355350126215323195 +3397087786775809367683818985726713074834263187298828458889483102002512283284602983872325517586754239568315454503146853407356331 +1156217453455804570907455838506537271360924924818686966590633031323668379608702859282734283827376245634390166587981939981633250 +3412106433453184147582934702569648764561354607002098617356742259624760387826342970038831595863063739528564750876654025034114351 +9541719290749487737141992118412804491779799111981476193385533300689670528815189491502637221085072487226634588032661043056759668 +2892064967147629869694614423619676513067951679311770744515022309616589783655846699918308471661872732172650395986258940991120106 +5144184865919978928920815222171229181997238748998964019938552954342118630154574987629428918689878418879411760377091395290416344 +6885105521575830610256425097707534533542028808834255387318107287078639558846084308180272966744285679093270081574095013341117481 +0825712842225983699546990461215024477130525988490880256409773994103713908818390693122587907689102651005737105310805023272218302 +5661584786775396459681434880691492087318430940497979741425101788956553140338175653002561188152737818909816437963790481976018401 +2333796064755969606150287094100220527676969430567702969130345130599373183629780646448254004373148786027781444947828389799036231 +2370159800594514645060430450080996614777495906387714719376787944968432214762676658329959973122495250133837956302119421564977403 +8812738504000258149359611948440268119800977088619535948947126735318099451090706379617772423943777604512120621890539370786597632 +1363253012488373680482054568974446439190725621429231276569324135313694462859720454323916583978487818838914751980359776090418942 +5309412448985837938866251311974802939648013868273488976753659392241870912692861488024484868367836148433592247524575436350374927 +9969514974899115014452925807661936280550953879875740514792224284344537632692977097082883985126798768022555743127429841568583436 +9127480468310694537875735497900079126170848463529624094796935948030072227221587835070990509651876620138981087391650544865276910 +0663274772751898865701481848764433338510687274568588217413099134231887382122648096865873338497066278774094796953376828006000126 +2223991886894820013487780739895880221686260123953688255289857284508367990146047269272253817887508457566736245360372546074724620 +9022913659536358863270572069247332195356175380482645548420742634480093147625717187780842180713151897503373416116801897541549813 +0040126272697263557284035143648280938089213881029883862911870694532327995219687847437429389232425351493591671711796587562791256 +4017430425461987380494086228031466897556783584467938250558585791601061306591861372175583299944343371927286490389555179774602318 +7872955211264709896492762009841470583148357109101357998512705454425930076065324016499469004837167664624659717653859578461687235 +9667310959423600740682018043806968632350609113790915003388710593533872466670325086871976600837835610439552558719613595783702236 +5280944927395295859052540423240381366808883628148042644568662154130067762884915257137995873057791352572012021048384019627522811 +6995106927652578556591681036413486142384921333996800868230237202393167717311762878194321699747706337864446498735182883503516810 +4894127087101054059360113153040095596503789486674685811347198640008673703554343464924312512011781208631600506972544379180978661 +3653914862578195810106428140030210938175294156340298752901536901851573154592330206774109827394766622168822827213223926407598523 +1752287994131637967328932739154812431240663561508299992489581255396838237199970478414918524473397520840434054432691053868383392 +4475254299662836002489198880413157763837585826054191105485073144949660180125415108179810953128183235091235263489809711965370068 +5618413924400018858280428271296438533254641173714166471746748160577329614446433367969692047250196643164558060988866789634419083 +1976673569829377798715474066924169354140929130742194843186922903877261219583485904128878654906618865153383966440192899927641018 +8533161119710252096705226091491715680664590252842043928311396400578644885931777054299394359376613317844464091496561469756448904 +8685184190096699452877940339075889148354699442923532123110999704074604303853725375307368452619373244845552599196580274471194639 +9829578543429362536437207266353476212156141077734490396691958840205815483098775874456262295189398128556239354143693110486242701 +9970409655619169147931181826146996933740442172396548865369872311637351974239103479278809874040155159335694067867509786798916993 +8934739229131395268686186934227620999682967502345755750450754377980375485746895673108969778424574385778481433357869228670933712 +6403853140387575318798294898252560753364466868740386419346298915926350400035543680556378607221479519818692502368546406996064171 +1053081251212324305626596587814428006013451031378599825941680949090154726066128761151070261252589982556578267160212779194125716 +2889755089191961889036525987804900491217472837363091046321293846058934856253393138783914577483067599584740608242898988136077107 +3871240659195668412257855781781704566864660291767024614816347476774763467463583115867939922590382680508695307915014840948963808 +2822134512303137695995007841922537885648949227095598324952993245073100048846948118672641286579492473549786447729014913418849577 +7115799710686817026544023974604506965289044028358314166807497671714007777434113787389629694840769613243261303424414900494011898 +7392257640112536311638754807439431161718219269700164766558394503991923942217206220355994391249150834799995352874004008637120732 +1738169836253384974782709569139552429502554327170357536474522232448622509720551967534903770682965229229310748674655279819812001 +1013488426299593714852536878758330763717657950972685023529624029812092380027913386862035092137102088651474613519196497838916967 +2365712331332348797178741500614741133803176085015200716152828860157706088237891249512432828832383523202673780925247731837609823 +8343543339820274706718254393402391405027365642281414672601521735599554959777377014041823630157212633295524276413560148004499161 +6041268421639300061742794731401962219148955309257726102832738599350862047986714418077392903665425072627420761149419742624568473 +5903163798089327161860705456299199357142261228683379549747158444438483324626130181126533716219751215937033507622299908730009529 +0388816251038799487192843352437014500990877413652618253793690269472286848421450299377527429839922951807455986611893857644960410 +6747097789593097432933016398117542211434597285816625911203020562919342940284869769635598001370921459163969583961849156409633978 +6058253598655377264266980145151402530578841715801191174134703632064724433621375404185347776838645447066321662228541280092856110 +8706460045506764337863851824656889505997963004840404222919355019179623835893581428684583324979366713626899343900308968866457307 +7217115588869155277775561876870949020701337732105962145058144137569073157203270186719489063643062743100706189008644101702506879 +2134117466607949382162669449831544904500037626615605917490932121868589822502449721652777432598078974018800021624932345792700743 +8772537472143251033025302537735317107162425743475413816038506301465925279856675607449722802889718409431120703912286958510209941 +4868004080038983558128526716248086993854257004538122139718459319434617070817759239833696965506062568522567601751636677225399970 +5772707421791629107161650375371738241195109244751411366251362152446624847548801413381446956815082676765934768156267696685640257 +3337783332137332131442737895629161974159375156476294489620941247396447219121871778030180022268049183913926389862808328015747496 +2718665162373177306261228085712460052227574664675638031209424855973371465310593845908878207798419801473033533102851536608498988 +8594650062259102571664509669089099570629940328720500583665382040942904077679708565306833479512196713193874213822763114277561821 +7763922398020531727099680865423760176807414153322182938644853758445162559723450312367220101572741122819976006288279862925391533 +0609418074163954317311535425632639460543917210847556877142776065667429208580080239400632276304974925905306867007809952150307911 +6324798067968629831501975773028726696101515750158909358262168518496428350181893495977033603403728917956907772455104980623840611 +3585603421384321409739580272281986370246357840985885096013164036482124665391931947880815558360671475167152552411643258730493458 +6674212278514790724079384829207583320586965949581600859999150201711417012504831581096750857003895316139028753798041233239967350 +0724838602272073110730847467620546382405164306488489590470927625949743749910925230346070277479842393578994351426637786933239473 +9758486324598916254826474547605389356962763994905059896268073096288839687527478615702716618454224618511400537520151578922366430 +0912492025822760544865884599694514920137845684369437041888556762598262707248611887100015044910247510922363893211117566916277207 +4970896832680655065722060515676245073680441413700428191825160119923053062012241474629435368642857468208602093702394160800952368 +0401643954061460061469538963715677841733067884978557675498904356226239814257513498478706711931106662782912959490144875688684392 +6427801429230014330280067244320390813250002494116054667882575684276220510978170909405937360209445943389405620264406898441269151 +4513961131947927648793953864754056606418638015682203461287515973198501950396575668694970608763112445843545620657603445132209084 +5756001157040952762796856519600056686459818285012859365932754153307811718432937457957972840218047257233193928872388202493970468 +5745171294650962698731398420701143198082563097452816181021021324846599968746630383827244381072653879098831871344995918422668994 +3440697357810684087609635992103602934958412129332726402190737073092334237927797275626663021730679412659760103153021222137031354 +2561873488408007135940986273685629006346887762950502897998328704241551747655409051903344700915847431066705228403387636950645457 +6677547618819900422307650673816956131536670246263056210725538571359966918216630406962626983531012334568659195035619701341684239 +2420112802529724475156900639848021386946999992189525298915178531829794178899375316321752347503745229384277622075513609101448077 +0978966580118766906719339709431650123507257526495690738543102142511117335171869720713914484644191091368983835287599510980274549 +7253035688826486175835885694607345249760996565501754434755643707038772708239856393717371875268895624588946691881982666455004955 +1229157575767360965832607989363181678043448764770403987112238108580504462888836301106969202711436540526584606392030520555629817 +4303800215068042550679859587110369621335305905151798020996167677493237088394878180209234115765088470219122388293468346691575131 +7160108019683761926270404198160152789263213718306724711634865022789627807005870451606075994736870542860235641292939358601690892 +1281304446363067443221484815128597038007892955460785907630142069752866701460026126386319238710932447939095864553352289508365136 +0314065667002957866858488765937097697311458242916562733459459218151551894102884025238852720369894804551004088501646041028776937 +6071040163745909418176019453877965601147459065352750105116872007917183143758403418504559900007858465882670700927317278238845262 +2057864431401330101261675645065867887669270980722933655585800822914522258771084932227437807465603988558837727049424395413086192 +0838403065062402417019069366493917705353727605973078124319419683057212743574597310780151384502291168595626908520023754345467963 +5237595147390251950945857498093768496867490339391087595954081288707206848402637964670896124834883491892023218043300933428536303 +9038575918527245239784697127935511565993493329345587370301152888215360283868124687096008034911588337723498130857892917083893566 +4326309213195337889918560482484162052931169753291241688638438611871045719430309380350737659700684663483713556511824284273648934 +4109675537859830665204217999322330504340383531423575355281348688917399955785319877102842092110480808916641344634371327017518067 +5976571379562477291805315122658077767583602698526304594638663614898936945525476463455648126285604724437435096863798048639029149 +6805684460338235182637901097297785029718931952225347991353757161498388530429639352729227333463916470311358261242265252454882149 +3713554069757475756235174202220726685159145970448234073615786797869189321230621988473892723703610057245772568034893831333824268 +6384329807418457095553971699330059290079181553384702995369891469896964859336812164228972258241580918221240870171611342340166996 +4238496467737330476039022845048701321182330582443048269767680094107459509184801830653831569366411726902088279531793681281083241 +1389317145760821195180078109186551396173257466092123382087137757380977821227017398905747777928583603397479220222412134551964141 +4172433396133877482307354312025003410197620920424049031355359918529883398507939057731022302176139038905017200234464598134643926 +3776455852500808911518410528880591214453698534859661007977301367112639128608957724121376845610991533136565527742397972171004531 +2169236758277228509207094221732914621136016542832442557119974462763405873279434363734315812872035133971702886820591857401769365 +0469858184126033613406108887661308724723319896168974862654713098974773391394034562612379275030863451007736429954454094411166680 +6170337864185847270755623945219598905895923685556701346236877669819713556924060508835912462997027984549870490684576221788467996 +2684567926478205893568909529013411584469062508988939792621227026970772272619563307265559984083650571137624804538833124458965412 +1535835911469765914801129303385752847166878267150358870063623206823946840223186885612380869419340578863726545596738437580294661 +1159345796279619020659424502037780228138063084481952522217422192474814460650570150288813492963026126513178935693001157815213888 +4115255308469968312956667592729481570942886979872098274544235236610800153069537830562688683755471055934166523033112817689423559 +9188936491751459321231439471707842349428821745374698747871010980962280880750506886802895843191246598782044554283344177438872823 +8347722755763795357915434510354216096329858917788023158970394599943300789945002208151468416849121549451743359325575523192943247 +0386458822265636778073946364464754663546374429692132747455551460488109624863176490317455770854516391720009905789081791014745242 +5441057113825577288143510443291415701276478572336349873718556812697918623782132025707841689574266907977935843620901112527314879 +0138974051576176017602134330816942995720273624834286549509110307443436271222852721817459586302582019304440451903755250893098006 +8714580715983710618302573323538613568787984646210126690769977327196476888780472811559022322326994280966501214045283109750894159 +1238724204438864658817267118920298143255297575901621515081809762078906592560699017605054545611366055292052387838303773046414151 +2117880548683675747162798543936995265803158300313026522266732130084089973850929862579530307924250791678839458713648131052866542 +8688866589649054666206289287368847639201089110726068276561807440633436849032550737263946103998593920752309241137169715090564726 +7992366978913349109203408811669845313806318145335482602504586472079577960741456598112230859758567892658655917499886892820156294 +9269289344377943028314943737975747440225464454099585770665233191895455121597963497469457088101021828673050970000696324467074275 +2620213720505096912466481021388401048520027588680889621938076715558063059090115474427731634806419367474059903683493740456083416 +0419258099585668206386510983923399175364308512576355916179825772896253917529784626455469847416203745543857585138629447495124687 +4926957394763419387802807256844093371043881188012601196274750577103452141865582262123425456983325248975618318937642166941469448 +7745531482212763608085822252151342022701776876422939982991937310338710298056659311711100694774221511805463875602371671771107991 +7622297042918195524581284988624821755976081656742322232878212092814401063399047757727446586120047765852193643525978220488403806 +7694553355731009082341963145016321622315287166497298675517840848325124707270827181601834343475566034855078916693975601380159131 +5691081048224248376219969516692299552551684485812747394911625377626308622713328149116932634160209525550974738125918251287523859 +0467965216958213222976523551707473542482226303543330237484038813545187688169900375790194437313184877958505335015185533760569003 +9313388170020500404223350587859029392425277844920011385363704844190916888907282100497715918895587428711392085764058004531043856 +4805593106127448601646584342013400666164278454947086863410933724954207481882298946080783397578430480735942749099815836074939980 +5829488935963766897295398693976065473334615080031996490066697080972996155867227149556928263586779745098746381741955988476480256 +5145688238158921108920160861540066499070337742408273263858244236306539277333029376512623358171655471201632148566471544471879593 +9921214229435926996318797357630113668084683534980450540625624914828693276400997437801995497337699991160179152196384869788686202 +5452970283364847951628816855350106161884215992345914720345452469417460755112437907196741570679787585797744951997013243245883760 +5291067302501955637725509930308800308821879902449540996764882569862895237545484938003965194362900224359317192536398128214213941 +6161982577027434530385173942460521391267875161999821213546453369930955209737128453903802184733420666743600784696394324963677611 +7259796660195828763104609417030954701404550411856256961919298349416990278446333820166279192694569999689157318159650815071786127 +1409252775006780923895660512761218733793242701073753044161926851887250329246949307610371367921483913476091276544943910212219648 +2024883815854805620725646102396569531338296559295702755514936293184048845778213281077499032309766928238274933799920207837132838 +8210747670883127249029109720500900846638073842072735692712299242482036224417598293305373767651397688387681839834164347629159924 +6849912340362293051521285112884702937904971535989594393302698489020568178389718625627324020502329354706893589619173731210032692 +7043789553739208010357759707009160597258827438739048435319844873364203025319025627611147486361041079291073784131340379802864271 +5630517567000455573262321447961724157507366377593234074637396775521413915860857523966422306759381837353927621547514858375348648 +3907374620037626404876010563369584978900791842626728009855287567645993864184962785602670934980414501849977057712840353440946698 +4146385141059123870591051497280881668008298341992956096236493101498703482256305064373982480667690632647788868461225656591267014 +1968850563894509480587740368883124473858275653505054105229249423142559298770511499954927552803754424222038587228379761857441343 +3593066522032907759239532950409720855609763959255411331452747381650146615224156577229227092417969990679672022642682488460230666 +4777284684581020086703554379926122352924845140673649610422757744893763657846499512591483929572896919893136740342514388775709356 +3278001195584405224574340518455727489287475412287946187833805383833415237459010850315390550944653852931708140442412147400013062 +4193626826135963841422031213601650476649922753686929704940497664287967482680127644394472418039840709944058706759350270065888771 +0496707764123239353916969348556102234417341754509205481354601177831190831135133718891421262586922485229955848858284322651713550 +2364211115463126546765721277139380044647678205497997698025656179147544409871931320460204274120159204240964415234469912669043171 +8252385686403049790333736390573303996823129984313927885511658858105668489196273816414521496403236288238282761677474764476093781 +1249545053006377773498004690990118213356171931577992909398589918937522320753583797192061779831406344206481621920250152295078939 +3709843278222111715104072968292537550135689257500851260250849491755576197121702408117922267107801447424437466479647604658838505 +9854374828488017805142065771759506637983062326803172669751646781389776006196509659848336558123328010610366919010236885546271093 +4449279473317813512456203547757437510023295240020059908269371826627596476653671730054696042131496333128833416321765418914320719 +3406255777740484368806154865989716577772954696365667763046622581695081667791363935783289021366520297269972594689838903064379670 +3971992965532938884360604600128828768832371480402617473079458005449743185395881312301575878592825423836990732749119210579499565 +4964414428994770524920627355735483099389790803639808833122768311697123316450497548024460387672711368021069867385676748771138907 +7272947208705814863061836555172890754692453812368615959884812383294177521377885043762530023437311935454983526848574910569784985 +9715236460972266941105456775641088939352379849288420663874812734503948765928906653066695278788734445614220395365589281481052985 +2219149571492569121653088856413977750911860290899252411873082297287513781665034941515671551141663335672214686872998009233611673 +3252445083210515790502427230581525204628936179674886617751355536540130374691771589975236704266655393863956951833668294842751553 +1066643170812591610511115847160870442604844517253754311969780356590045310298592533473063715395834561034249527692096332630663031 +6094678693574789972643719059293336351890222699519292906160704354339193005774875964485745420773135852897610846364640226020416376 +0078821325017540745977661794444919031229533952549930041702337654768594140134838827623154832919375367293846998623573154781662214 +7359805369711475680734455906157477769170543397898162106347663466476684547058977605857152935135397794083854436364067424905370520 +9053319467618246985715935346228519691376765059011804822695041370567668817595587435020117179808441224160004516561911764361051760 +5279785887324494559284533174001913906340668600486179364934683183133265522486775587610826495582037876134991475823478304604306409 +3941979376926208894324337577802341103192284179207690346815460983649049917703085665853453770292030524017689837132097566609313622 +0874696648882529341619751279943169854047021416531564941329234495433297775450795376776860235960754121606226960482122943806912723 +4403142549129181418798689888112583174439363438245360666469436690361242515845002333293117928704700704567851599171947638983428803 +3086178172887575667595570158882926151846562563832038255451988723965663322118638716096644911129577785347672668155756960077259260 +7016060211620447349613366392265953857687262706207804671997611538526738050334785125634177152240160503249217159499633364839660140 +5489512175142718110444335510020717260552562939322469369407097546446468340557730058147393474938996409762845800786386613781180803 +5316893398694618358236834304108186582233111640280225864142166804368748966281116139163914774116490280764125236118841244780654205 +7528194334785274348270807743401184076977359005808989097790847628594537909545137710117536079654129720175240043680499382931315617 +9670385576675265565821666921824539041489513174876467496206200652157451658908879498586135223489695875901066307973744711118179562 +9293554881752014067237144247792025493783429506129044867060712220683356640982811741139549073541449462843252209357539966735713385 +4766042719100373229390546845449462827735583320023428894346366740071025652090167656518273656932135482003817661060458673642777968 +6301526335239349282461856549816100522037177673330915447520746670914149246304276129215650553527297720437502141660843474787824161 +8981954968443818080133345527259601739419841565729583936192031325311804839597158161812415269217752774138988523259895019553556494 +1375817110090726843596408461202879175175450080051006863993971804570798947994555625585878024069026053117840656838335462247825434 +2951238989342425774096587516368372195370856662387116086488893532196578602234813604266303300411633704142608233959411992656817117 +1078366109359744038481314864313226318060249081213275603047710120330297564750748020015170091244280923403487005151336327967793644 +2590644710984063836471814396137903631095128763841800600303330827770204519245855428525596893191150792386804495821205935562805056 +7587927489845850917488079723623306056569190541982982580007185500509384196528055437763768159799054085157690272308144066104571631 +2716691447638059359782976991714931875589838509515317643963169052633209626064370311932621633892908726622398410656122098501462508 +2187341550744516068766043103052788709422923668066123030675357066234501008353745371362353282261896383609682551818558419836467474 +6386821883063127753430057004971408049021180273353275003607608462963509731380707112452702494852068512020333193164120541304971821 +6414619855917522699462290111082170337250489881403447602549155371621815281167692543542521085921503446688385053019234090686216914 +1514251753750908058132963706101498627247758343884090328099690893144318542740931341743091728225255850843917610568152776796003007 +0586747600603620622562637108763020672642036425806055677095355709416205188154703219135342081900691181922293308472380603914641966 +6943882703466448671671347942866066071729197097929636191832480851465967168936588557508460013143074231989501890259153468168582090 +5106944931177904690443492972535629421878279108027478056702348421231814228319595001969285380236108675594104178148223368821682505 +1013423077468134180344202453328649263556597230333389978398486618230751510288831048067335293062077986298410020248356624260186784 +9609742549854821633067526470397698241682366743287992123678759953855431672859515457048320102063982700799641338555460645670603233 +7227510683690963888209842085118318517866633639207568611885740247627869030070367221673493120062595703412511430540635725690733531 +4166904720216643652362019185541125336279973971095473810174484610634739482031555699998920445667293351815342116856019765913024721 +0204685598907595748165591866024787377707833163733227729761204199756529370097242687722444149797918578951904826714174399654373078 +0029695243252514591043935308120948291772608227171634548103690625246068887952914933102467669452782090604646361027594958463008648 +0606043910237689889945195721316745105694059044358961433889516172023390307325162841023925000976730946622777686367139699337140288 +6532770425227279669614506342121365572660042996371513972011473090716295756052824631404973053818570282987235859556502147507091296 +5784383703297394066201455976072965897350946662861440804630035111722516300582625065762906422553404492330383168345359661866546635 +7755352359361297136322168309187217709956128684689901207140385336402261861792474715981622821266358097679527860080999492143093132 +1766368206837633471921145548103904997241216165904015409300725066451342178028580494490424756975699571333186681161146526160888637 +6435676017243653308531673583521916536301571057843651379516536378840317706213255198563089909258098489728121633860286779010828363 +4431604721944445862183599288814310892377084499055111017374193726585680550914726436314439993180521094601524845475777232173548652 +8838913044786662722923315829866963645973025803566829523066407753524509752981118743428995908031509623037684132020735808440759714 +6831533201518779101708294838185079163952906812726056828622166710833228863087344104415454342957894082410432960146722394394312810 +0026750412265305432804678135281250107474394107101163961158631241381326874456825116424953228542574854725058161235809819422717951 +0530095666016377728309821374273950714947770494099333703734720428753672558560341529622248519497385032763976784920070775745998492 +9988103492323582802831123529566020729884479858348071107686123450154590877464099472518837056085048970237891379355375854402767816 +5730632914573233372640655739395224654373166501801926999740491530052469701082483441902155873112176686047852502749353152358488650 +2570177044053910045946498496953083595646140791798652856797075918041625426521730745013108556438270557756261535076617672897082024 +8070822196216776547306746987791806776779912117872036753101249436897151550298686891155685227497724326515850755173992122017730073 +8834834453446142095731369167693716173639921382555584546233080228430284169771661090869805523170856183199180807458939980812347666 +3374034628848110181967055896853894757362330956181597331342944383294532319770922905892283292682026888125967189041997170645906930 +2816796708896328193169753615809284330192400628212014852560752820595983573685822703155361815716715825780349446395688288981323009 +7068534005360767613192042797492589713014221879034298952445175191916602695770589539510033643533276163270684273301159972772079846 +0851847960381648176955810985408204753982844887706344041815181406997985867444440863020286602964269777581948956459433973575818007 +7244153065545137632239977736639101263396745129055793110973443976621943425823784182900120559207556057714294191011934647831514642 +6220112071917364308442903062123692544853904143920646475613405698807838684964691297033599042267911174717943845099876701146499041 +1997737579617455507368079950355247973464185824287192636611840482334933626092039322468638416062106707636638684831343949569888001 +9056473674062962705649948566947590595122246493016055906506275120659977645801864949778640366697230510631836906881678697914437413 +9851293934933351435860731679561845700615460829332071112719063019501427379839759911846192375852475742108532990355637732868083608 +1981670764831163220456625777162284911174669042984422665330847096514572430663915704539930674009046717718308791910081803605092076 +1436167228645743999726495063433059497403336540806399989879727825522625780324646087140141119024144101040594738802339934675234494 +4511331339256646611464785947970006064997210981736418145964184001849876673693526718038480153571548117761471182638795979792615062 +1795579829237064976093578272496505322533909223281608272636931019121795380635442871500788593701642237770643966129586714071750006 +2259558543726883196265093171148297545202455060457641437680621020678468079454735539167389894892674876922877875836968284903103240 +5190322659532635977759442475453937445078305277952610411039017125942798503797185204729242079034555967093953206391753749025286545 +3346586896166410527904055884469771043579999692960199777847808583456813215611676699557144873569764821207184084966272170201919310 +8492812746065013433267691323002027533207826063607337577540310499920611687417123621076705455447051576454279273880574231655581484 +7963980271743134112154087260650334248257065896933807620141668762775373128572470229135441642876072741306062133796013873900656717 +7155588355012012778870617504567123068873574015355543463865010611682540901252618661459039457810933495173848721472502301449759789 +6250342428228956319030041026630301654869718238658413832351916020529083012909954188736775565330222063260635143292678318707172682 +6306362489099506339243174369018743275045301435603437084640257826344578993743886263071878533536813315686531934206065891475930962 +7256078764576049009416184871748648772794832357694303718710674780587401830972226331756569564368514137449167545781329158975242668 +5137011302637382277771122596368810380574982751431852882121643844964841634725254405565914232138014950212916177841984286676442765 +0130766950573613943351861223297588274240988343477886895038747758639966388317215304568124673651393253970716618528199527580176531 +6359126988671940758748887099156058290162393017636744749307547677028026325587696225823070273928497980385751428178855356190336058 +1262747164602205208071416052898259778064629199619315166088744120423992672210748461774575909144208599043872184758764391922991513 +1637777143251220955861291729128344859755814246044289400356284088474702731048933137147907743901585132956196475715784684303014662 +2779656199507816022148661387875419531355613321583333187996164541556836852609250800638246651009398874732599973734012124894691052 +7354320128684821812105410562348993616339745664287363950112497961551242247169256027249678322546929448755022227225938009677843721 +8972855810687812606653074585753478301824708270260970161604946179349054120962204468271996990113102973342165999101606421899608935 +2427496334389700886512192278170872300796174251105207296701976843139085003204747190192754643836301630137058164840301068688355227 +3959418860712955101125551436177454788270598656333682307048221492143694319890230219738438292220497263359673419823698673057872078 +3013408623490314068776627587135426768753482653895410991590323095157887499728772798489129923069618646593123822639438579269538635 +1908216328408109211109195709992731650405613610224189255235529994289098765621895894667201782074632616819297670322225038809119550 +0817943537763128333901984566290468707429051150902489063403888015248830107015875323502758058360302496764604163625874755344905059 +7334562477121670573355518606105354751505482715112941146764597746549856497547302376364513456936736713783904762299121613461158516 +5283407076162674672773440443861453333470684691925760891504089347363035836279580135355221825821573398468104182485287843330909617 +7205284306122028970211792875328746564733992888131805747056055043004240391649557442465694153748887961289838987106491544764907052 +2500872325568857502281229098611844366313487869502518915819858430987626410127129066096878146872919622682973073559229804271266907 +8295782206872660046903141337058447164373782585028140797785111578071196136068281762757693333793450310851105370333208052458896958 +1725889330426145518489875923548346134621897254499345669060138933732620752098808224545684009099318550221718273960774029508377011 +6086360263760539829227685105382480409473035021789956560069988221518928563686053953426925951987832850158619577369793062706768043 +4352919017742582830329403068368693550555003865896903349651288061511525716234272382256062983241592888339539171693959034050993111 +7316775446918119981332285689855871890517757525471516688406003916733927585058269512316355228173253797483394637476145877140830653 +4449523419101039158408708904430347733804700552814623747680680614331040890499889250979677397459532347731846180044631683772954511 +8775927277284478472403620186389038850970249719140155845452372844321949705137084066166954690611328982193781479003940799798395822 +7036309001309751772232372080817838422988947200353430157955341599997125428100524596360021893214442902724490483771053395386266905 +2526553722458751882973801186174660169500936459737553375965211051557068305755252365586897544315025598670454207459745656417390706 +2637149880243542713228217652476736046313897745886197441813742086881967480415661410956495847064049122729695529600136287474175290 +1793795271658672504903869428016853432949018555098626397052181786382888682206907544219009649133780351002110388824693692103301828 +1084255129975610497399774428514444229695501619744314654316691842636967058501657387850037557822192693461604997872810120193484078 +1690635325513958119210187262311486075808664902577687177132226261921963946973671413085629761815694454894584772804358916692902290 +8584983060051303667121308176671074873807110361225378981139031261855126176018462516372607208578426174357538994152717337719252523 +4278036428830763724403099207458044021754910574858908048715320547117712294990028756794510860977637952837840922253254728458600984 +7883825345262132475931206135018623229949112586397174487649066363051114417355320278795734783658869193239014023523778761960029033 +8695026444117478979408146585926902316341504795145288358653821833800650855569985787340203101735816503203866669795526287437002033 +0245051742430260486282155254132179648064481192529358125651323989892545661828432074084630060930677773108561342125212783387281955 +1156534912244337917835348237158665382209292463641851049571853921423174805869049378131731211644957860891749912005458383867541676 +5778772564492822262562399502030821202632651082331648596186198421567114506013473886361019711381249854838415342169443696628858178 +4018937636066081000726527827443126701548588875106206226537909295887328559668190334839725133746234608966652449462868965060152769 +8173777546154775498367210355738304841393638757271541666703324260466614672029573295838066643359099243819511379078208545782984277 +3894531584135582441443997552974471433572355979512626259310899921004753808626563585592085956971397408889173848312206978387162122 +4249778224435541775834672279093907849532605623442929414275448734738794025759189569362602709330104719867391830325898742293638318 +5618458472230917207115381643121156657396683440299320861523898119230249426608095966989749077626813445096806473183743262392826169 +5141443017081978141752639960290174384446691905687453519079674615609648441987212131402698830771368709970102768419122745998384912 +2946562140012561122001343127839838295167741172616748608003211961781629115805578417306196780198449660660896672943032048541196007 +3488478058250138575080356458275864826075120035949332139371412714212223138848322951784969631050003335580101622399775487873990978 +4317860282593236100733831872685638869277432762005042347567404549772458118340256499688632865717895145858533810557576307974444603 +1928173630042126038543686101259940626688530747112695018229893249219604395612728537717051374474126617790845613836004135628746556 +6994181584676204860202674587876078621483635083475446039294576324194007589602377577833162510613729896861820149258590200450670657 +9591153201889299370958865743889668888080847094554165557463441635078117012681280739489741681606506751718013927402701136203548044 +1780167360508315641369667116901861812842526826720765686979668702075706357523197794146674984683061754983908320058757429657215020 +4621260027453785113729584439569855185620374311773708057945213037359358758689719104605870656508325190409944963614795797144191442 +6611412529823091494357371703456481103154272600004279607488151916951301067639113190114505272247774489140541745738078452548556834 +8106748866753253878302577929999224844368660368702977874939897082451513356653811013795048262545973632043664542271741351318143337 +0778069182951962287178555694626059563658908945272131830094793792647180018850962827338479100883010796179956428416377824203799082 +9473023880877077477396337686061036427727779399244945597262198872743579391652952681609513942805826872165303709494805916573728492 +6294092277154179951451094865452721844277717466377028709711650186357899779470166784063622634966195721705046717157464710442807466 +5804785796886616339545161571917895816310538794711393196665470438339980915686578699904466139950097093535760550147855823745768311 +4727439998296384114816286463820007469707513836075275400610242782096499770391027100262738743742494025582717951675320717526395925 +5770688225429387772370873623458820190254941682623110262393319797303540835367273621810830554446149103975368995431078077876936297 +2405529175616664747550451981407708416427503988134127731468009719622521436517602196339409678849267725633683568413854707742239684 +1747116986005191343502956460687122356601160408668274191052227378815062922961362392926820216609198684542476134407735883221896780 +2662260497987146574078784874203657867733135048405416920171958838688884627100473968981627006284094098910015991752992842055879818 +7034398681569535649106832499610645350629642323049347631261095876814181084980886779188444300173631359422796869708950194760718387 +6836733703618557086120703365229061728453720426186389529699692510071571231710123347703658534972106524417884249218380254553097325 +9819799847270574690752534052463450597318916271469886310284525865231260264874522733678980911253701800076986898602579299994595517 +9135488779985085378045629972341159746040904128068784460666062698063117787215014912137720162075250475265414449002077183911431414 +9809851736195297213683107966811862847637535801414773082875296684360754325074703853487298740359726050853338447561139214515571992 +1029599022890794387902179784268370369360935567144903247409435981421651841889958031240517172069175107596477581973881408689860362 +7400367259185579353010164869469689488102231791643828135531761198096403804156803391787688285965553218505121457658933398225058406 +5220283029470788325194233641742163486977117357701064964101876499830666046451739488161844284719262317454589282938926798508893597 +4856368375624850629404156042966360434746200563963426632219247842217835149470275034357811087591708435161482657165228727592100816 +6253772836778061529203496032750827006398455227451857109564867169437129073006140463432201664058302222335982176666409479667581160 +2936456922664809261586171438046311878472513284135606095783748274074803411461233988794425470611010249655925624921046206975885663 +7903340067868061097623144373319704434575514501755821812557204930865063397777392798291309341112700415633886191910088310537801960 +5573481392684970852911664492959855735295070104374454536083712417507044706607037494482191236686504544140731669546789850452790174 +0904993356742978486159542255719565213826027717277534463338027027906360296595539148861118855418724089346480767402189286985762676 +1005223076106599446960454896515318564456413560395537757983458843931854126284677517864393826744115625918435152244510791809692478 +2164405990650873094086602365842140190819719314893955030230888932507331906676191911961689961891255808931785984896260979975690681 +3253727014960636085246150995307788585817654532411113862158610700251523142672098671368947509829930523262641611496136611155754050 +5927873322526687970315921879739844277763573086829985560339421326548635360045717801382027508629836290928584431149151932663653210 +4760858192547745555268997678117732829237567270192822266668837972286266876280740550930386549524048812443132805094528566843643923 +0552200604353097511107833851418517885398565041104987050582967853637644070929822812409158588536557876274185029987107810161351297 +8488557995995493390642962339468082402922946581115285486581085810242830297673687274512734725802547502660334048415579446341045330 +5709356062023991965872528242196958896198910586431524813751700975464715731688842846588438403790663597080684309160359805580967928 +1809180118084899386382440090650505696513340627765123157216132637785703218992306349927445013022757227491085193637218705876572392 +0750092974015461540416097560544979024242793468547428554110331500932107320102022511854661891492261408877458706999860502548130498 +6089719190070430700600786193146021409101280174362984471307624705342744373049193769815102419864609660529369826990237068799451547 +9880823550824400406858764215681272127616403736552454898294728046365136835488231881807123019108941139138735003722461424915329488 +7529037321181887616470646776506017170053425836736636947752180676382271801340633168596956291990586642144447067879824547939050775 +0702265125795249993652978449111254341598591409462513412644706330823597010310419837016366205284525093508925291114478065520543206 +2486005489914656200352331842636430195667576175939710807407359823751196926836356542172998603833007594132689750457318477364742581 +7540614426291493245957654051180243018629278083258315716499682711339148507426602166172200768552170520220563426401305349960660512 +2404338617929580850851496148983728094122155851959629735499313721174625488662780908166750428714979229469660586277142190041914098 +2734970584167413642829413555542927092036718157943315563661250374551603827984602155759774285156429894221533968500275418633637182 +6560220052859607041833971976550951076186294969188109531953615241005064465315852535875503380591519469842863315620903120851882868 +4362109461838990069490474891539380133820330011017862518437725479693309451782697543824505868730424985500423739583859994419014638 +5084805381905175100929792755321577290419383142651436157340596535511027236705949268189435276570853108955493181266234603455114274 +8408563281709145363110504396918026400160693545535873614031123523353385515760396989543124701606521301192906747319853776998158894 +2312458699599599230972023213413420672303278131983854442962403875724144311681960596932014490316204359247402654596736629024793653 +9839221740989926513947745565345687824749236841443758276831531279631822480088615812410461505184335496692743570654981097859767887 +0148513962240941032376953093608164659349988907238073209903158402845914764726830224719377417036845150993399330628686012123441747 +5919614485617148484720530134538022911021657584302803306902527053421247675384565988567910916387370777305148589468626006026739483 +7159664152823325316769176118475149162428707398939203649766531576459678350978394525441052205823105345184421463902500121849981000 +8288659993085551546441461743163304405139009826361143974328223331967419739647241073571212327801960556484524118468883764120169524 +2246338306775029095975369446186770386599660011384020498379465046982275031718873753405808637548601707969197710675661519594764281 +9599700671171430778834023658375047244787074487357323294015535711745803913257607799601446000833932736200689998469969708376723008 +5081943791810860552689101511523648202214658112058332928346393590013555144813066914194030280115978731407804149764646197662984115 +8801177359464553557557965020150275317953374369713023241680758235462889780964894972416089799047047809594791811478533742030190073 +0665429844750818754679057503636454736605371676903157540119156106676570191787583727649819947468340786574584101743147082806534991 +3657977605543416424987117022654696293608374996078959363230667155054631271871981115488592293902083994811357408917297410729122498 +1862650934481731910956432936712254764601612734362801048040108459535230079759939514256093409449391542317896091270323008227781776 +6653398313274972684240408723778161592189390653041468423088421653090661249171357282254451145484842067919237970412388771640682294 +4059821256052393643115972017853583376641290368154419793622859038067932722073228138326697082834080833163668963795873824307443161 +4885945116785653293180979216652517189039102100870721593874386070396567484340764036310998065513336640289679790609291581823883333 +3428604720660372157112193405611757297738411485553283296939642822708738180393410023516446689646065368009275111140421244624003704 +5562960052258931350563961074454414104687746794788022658055770332756970270739126216170694306943534685894930524814764997593650416 +3008384543733504706776478261526614225467564069116098586935957253917385357687398588155803265872788670839186222042335779464643037 +3618994828725096012458832876031499829184389279845891008599618731986280732307102787004526300770299430819579608003697211024173693 +9217086377524454012913218100708405723232921242597498783314818967648121936298190307693812351239182876627690699979858904402641018 +6156985750875948360548164905855940506817600324843389599811157633619677210306664432514278405659673075559217581592839918628085400 +4764772987507938088705673246656764726283775492007659316410580014942504090614797117731679264296150069410102991752421283871178501 +9088021494611622294962362961658529846402973311983387998125910749644155422299932240783260368601058914444067830424352644504379196 +7142168355025888186137900382975205885091374237093915396552747486547739152007548268368634296637427030850473757422944218387854757 +7616183325502874293153395715965826837360328123096242771948215374071981140320994435687149135694129364024023152186261051421982087 +2832792230789485819847349814552874874835409931161612591863141976920380812857719621635320076324767599038119716738981834869773741 +2754959867605262800904073710237512445047870616662855713005574347858126481109562377143573312098011061313182520482126994274243272 +5944572495216446034342198294744331850728989442478427239279385418283344155306850003896073771784045248648249808863649089110085745 +4935039678346741118281854023639235666178336524231495974905785711915838245296715638301791203620715851604930385744033263165263809 +7601373980902035512868636064609722029939023536705525076519976556652492714730630698715244783359777077548960439099573621098774644 +6788103237769355632318040401274560065924878241545893694505228598734431875252964572555463844283713282670737691866931493869606535 +0416187164312031460323292595651046052380878338516759425813027437945468646970166296000101963391062426079829338723963248745406623 +0923089990664161924027191610177696764629811421643542437939862866986835556859764272302075103487218202328441244550298063164681875 +2019867260237068480077811032787819165579411546914115220954604696377168121100888419574969311251245311564544879246746582842436916 +6868613732260412990938531237301911324073055318005385120355972354500615925738636280898984348888758911483752216818128353784893125 +4877207978980281193491650735689287510067435635758992016711009033554062927257985428856499572775171602494384045603122508003854191 +5729531014485818371423575856151555802439384687195667542811436864469460636476071341511650757925855405897036006697261458329928553 +5460991959983883494783035364470651652782232918135295761659407906592669701830570881217510435070790362298581095301898060905173789 +7646351028988526172675924552809872541181626761125500157109077472362508995981523449708391893239705562284235588614429905027555758 +1619134181392874715654091932556140391724453901036282239744719175838602787922700433547863998561669814541630538374359332841359124 +0642132589359772875036014752032306706353508991088875094683831897858049315214902256977825841683325136398731544129046064024590934 +8005838235188541103906355513876550842936138467720245485620712847710339766152168792765762537490548824247757795430623230546063661 +1739498737086961685166983125262113774813358028871712079307291894391835363168962025939984050181882718770421839433587670806624476 +4755044069459609604971745446900378189205860081010611662569670882836262612421135453240273781870602310532514360568130085933991129 +7924290416111768289187834903766663006016528601174525638099383915633909474617753311810491476284105443999724682875652267118724896 +5850681695306617855936614324554272763867627188224507120915540200480120428647332751636414058742508052557803594703673781795569627 +5161939368398633733681924028671740682293310854421624725144685852406527972897380355105818239535806432392343196805358469063293155 +8819559114109771559602841216196553885494830284200973490048941610619985614867118986891106961906280843338752508763555675909702206 +6241566490588598271025864796744098875607062200785043619624462705052447512034382219094774621478633751781359662848610200184361678 +8704633729839925740538038689681271982506814499991016892558765319948519205733093865854380442216165127136281006009985719211033451 +5874590475603382790640233559612673482775953839089864470737210616394894296028059828241641608622138456309335058488482647911816483 +8528577480552717173959823507749229677977069134280953275629509898562844929982109573639706635205828305920402590508711781395835713 +0089930344552012906619817966789113931607542416094931145768127062728799915073397115668423507229674038044945322045301649766598530 +0236619236986319774758924493517372212562285619369190213778360087721105343106507409406716723533090029620699768530871602210981580 +4900295631035231554608058112293217835929574133906243363911069130644679986177379278388410792340109628431007635443781281483062060 +4988368133324357435318270787600078029431686121284484541827394881261897104701243599451161363480613963194743733844807088688115634 +5178871803932196297861340370803170767481272576611956494376594793113965439245117335478620700378039639755713637510132362926267306 +1973846871626730910248052978143950536530975380151232887509932339881071793647054810578672393058114957563604971882394961817835491 +8978209292630747251522017252062343300917165930874691119490642832557026684811610939173618238354559097444010525631775837097840951 +8326182506155413337881369137900095820346248710691175947937982923048432643166491650906416257503450243786283464120124419550826667 +6090914463028322098876359782808759824243499333411218847428154348072847658504046701534863522297418259623151406625685618209367317 +4253745664431692653592530190665547218310374995530293912340336697232683775175851639464892393461388136647485464789810252552522056 +9182156878387604674503124667923684369744370052287701241975705851689429509464670883347575783846313229293967680582071694204051095 +3321006971958449910630372247668837211034782309154572655303854890011708581736868651280877351904031869989741999700057554577154390 +1273067298313752548096207203603148138418750927315375729523637857281549085842436944573932481003443446922848528006413314412427332 +1441821872672191871420138772481289303458305298682170739045298765824678050356023853590500427850176428627107627730008396343350473 +1052326101498218606100898713263447573551827490516947668567656571319040829379674136592743558516074455124317265474909140902458087 +7834541971992916704455016525869622512851276565353916338983507230882585013003793323808346566693345857527479053033593116284574877 +6838252895614149905894777313466208709968773989472894570857815422084908815232548928189324969136065895039808122393702760781384523 +2929303794620328740393974372454877708805842222813897741603177439187185228877406834374147233014054610369299556729412885884560745 +4370856078387921623155622787671852014805409807299060561419288539719819906697663945124473843579383816264514077377269614977581685 +3394601383138607507974293429184042736065549935759370359893007688911547744095191909676349245026886870781533913476330551278360831 +5351259816322765307961191402595778339300322026355376338266384698585109900534462235764152505801492032987502080039176402011160649 +6015860540766825761905783450339596728965747988413885573654798343784315606643443103943694670901175274103478596479365688106195479 +9733596316022892971509096088392032363480840038385290255713310540683102401152243848292964926264212842555582110823197263253672828 +1062515708221923559386659794495097841198809034454364525085442636233669203446069926961726048176600711294973993391741061849180079 +9806127623797585819585111237146636415904332057123217989550966977692535517011996512651123910136604576120833852264076101548649068 +0894752723723477938861483316019471992063859736335516787664755140329957520633060255798252299793172160680450787189861312439796670 +3913142052442456614486902320774033281367626065329089488796925098543116615817296515061149179999137691128957532263158924327442783 +4918858536678070436088759702284980929577025191565900433342638171236644159755504144947154030057873677997786393398810399270491800 +8872734106335380929798217774610094691386997739307412068494969134661496618521118209935954632189343140189392135323738890270473225 +1245647673515237131109490917571703008777281273557596852732435015863930630266628924061119974383584899927931767742330245961719796 +6852379757973100072309834676038484260260899346508201154263890305770366822928200041404900931901074539015449894481478124870846606 +5288592940348602075775045536917167753748198797428454287343716300518081003013742004598309648467119358078992444950790157070897622 +6178224434793312241397484872165819485491096197408141733026367177440768594738187232231600889719889461459739097869779145699996070 +2192775687835295828387187709692569714086691619295450894879843196267448458847228413092057333128571855124524447189613776196209305 +6242914540023653075456224110675459930237745861318742551814072907467664681202170417286866153184100097311858010110265935844444511 +5209497239417620919469741474768380931492201661270471587341653498268177621359803201248700031595849957309397980724999380306103922 +0556894331856087053327675958304000445533946185075415894264871473185811965493131424842414390488954096468377128994425485984798739 +0196159676818515225003292466076872625799134390488404188280002687542570342770954127173812843410476595231005479765885421327808255 +2605242865388848411850848235973559236245406311003883744452617685164124063273300160172061005325886640047067612731170493761853684 +3723865217540793835273126631505051186346898929963194134815240955269409964826180214819749886514216356192979128714223656587784030 +1612652380049586827710856241890765244152817303616193799144244842589626119110544403111366867458976947676427002814778923808924618 +3631721504060950588384351554676808360758437911729833313655406741373502411393332262640744499827573245395519111703225362606878417 +1363409383247041202382518900846037480203513170344097014906470642027432914804792692599759592956926861572014354287205548305433650 +9938363167455625445680112095072694438021204263665499289036436205115409602435745285690360330318532747799997172095972559600945294 +3849224957526742255789376070484796163223388025899309472042686406132560940455845848040737742829859477224469824421878404438255777 +9153640594267832855398773722288983227094747426234571630063269545219291207342356377829705537624593717193629488670483397161337722 +4742683466675948276596206085950351177248268507712030644980189541847011659976997997291786802803131893537147312873666734632318013 +0962031832358588577470495716330583413563622072795533159442347186921867618325791765612531566938977664106322136330634216330706485 +3562792846227123177501866289549655646263423972314841727623087546398961182987448990222751356418413165373556965561892665216006475 +0506481362115739578595809306613922949438046074388531145186498774188751322860892046433870735773039483261252104554347599292426213 +6629076659887419139215806756640799939588104500582964705062365481405718348479866775939471858338157222844571173771380330117254323 +9427611277124624517129218643055712610406848999072749557192128496185603527876575966140152280783898310639896801498077008734557576 +4949349263979842274642964286301282439502057095606102606822710846752651420038445554041921101778203644249576817202379962365236644 +9000537778793090403522534841735088671708809700351632320108127651758914457761194719832533167517277944390073544087779452376879039 +2179620974987315833763764525622394287879156162214632089232065378247731638836108414730685627306496030775755582279275263164338314 +9850178620109230660864047257606734366763600031327072575073099542439779650618140023637464021494877439885488556217335419454669846 +7274436451730146692806124202594474275423757115268107978580315116832524063995327727093500514697813597360878942124695394787220781 +8077050800020141873083229149343719418506939350096709806686454834234546620350690053797423042331890117409969910833834635664137578 +4222569085314829230694422189066171115029353200770994620763730497186499678980036529016196644500138184587387164123266827728301061 +9140763168998745887384781477877234025340780171338591152461030018239430263211801503428946233869503372300942372235304400820946852 +8437399467538616927457301639250511729312614497619431195080809960619969552298634956256648869707957943168309430154835637684617202 +9927888920815679261894236713565064562156846670872463967287481642034739020192858148286147625951137420641797396365115772980951219 +3655403238951645285818609634851118279549531888817205406539748497770277166060720853420241694457643803188820531391415953932633187 +8096429698574955422724412934129163958768439154851606366794537099440778504483170647334793461803467077456150095540043989710581777 +2365201968161103470136827409580883843970089911974172408580391996075540014445987053836291636308746569551411995558157351658739831 +6950345059892537018933018077820506883809089865363951638996552650340293111131388149509355326175519823347067351884531383182623006 +9729692983578755541105852708900437725294440871971817004794137742625651321002377622365955498287088740364399525852759257372235252 +4593851734614428386795483124618557640086184414899615960895693770722697765927766996280493365204591488466719654529288305662919475 +0122479598904859751605583470001521191650223688479671370454989314043303051942287523649808609527628133476925823025842389422672084 +3056608819600468295745423499468437706122458751753728982506585826980159252966514250379765257256312249132321573973154317443215255 +1261003464887189939919598811711885508553376196973665253914113359871227784508364309872766800569364687529405313394507842277777341 +3335595614255964343357224505993110214988297629416503148121404268247459966936550735839779347369294370575468296662036041463516505 +7279136021331762991558929156754763672783232261635376259854674650491435829901582103560659803340084857348232939069746185793481086 +7001312755863777334623418958360382433258119385097977803059882470820602915102809159265450380632894446941317444541503087066649605 +8796169740654134032665098260784549928283316353597416172219489498769356834724310846396610790766934291348253075369200618334478026 +8802831163686839169667839421840117736653893219551959966388948111069365662007487678001136916755953605936920644573768537311349838 +9739545132806507773311726139940569054566559814706558048797461242055138311352937387308888484537481230253759123043684421386096698 +0953084370435253065135920609321712138864781756707944630525539032264477854921495176982483322151742604869890730064964430072465893 +8563889642871990807691809483723365775240608384031148556634799934500723149755352447233079304887498827996963939978814660785015524 +4422068594927829463105462477608520539620307156466022889708603842952278355035865136962605151617935839688034493508879959012432457 +1797597440197984699879058451944014171742351115567915351330880426124733117964473476818687526048324044778050824433512831279314278 +3544880014006914530017330247894174199038635755809673971423695196336512713364189925640866570283506889971990951009729565756092473 +7989020444450139152190142450813270776908916050703362005761467814560672206433992898775383186508883181569920382329721780736328572 +5963607248916565843748383716251646836534350100847190837736559193434644723784998135363197913246364668420334826697708573880522965 +3263484959293045146013061177283236124170085863131765193651484251463844732711431227375764646336930721004123585223994485289918901 +0583029668150873954157367992526151571563606918837234329358185868795166989340614129979830550547132857672143145949742222265113600 +7440572366306644814358767338023644569585694763643144805041668167574800647065193962831865616870056333413559981420227280146948103 +7295355536699420073390947523011459995035076225978883439577116325646324872851036992256964558891639573786018094493982547872559135 +7129457262838894246866292831053823350947068553550051302734533231301531811721807950765452389323781067242015285235015915373253500 +7003661686534989695171145431734885832337556123063280208162395256598378269110597151277334587048349487450140097618803853206266757 +9082217774055830430004211027285925285344275476747121049534781396260863522010542259349559050699781590339007899734612452403479287 +3191667292995301649109040619271406402727834137378627737752393244791408796163577067403946973388491815614922007647090321324522320 +2949712664738909367012483650928320008049914477514031653686312257788502415066119921871956279807885221949190218605724017500310920 +3169525625766817202209440542641629818384859195018848363928308881901815336912072399651202491194626625193357131514851165636605801 +7255316665553834762495164950088245569336300767957000036188227515262775800565384194139225891768802813443198621045801692291233140 +2574872647913296219947966169138393924496399035916640976364671914696565353222749621284958305509352891507065537978150518314618840 +1953749417768878991833661315137426428652077503145679920658786397955780176167247358992933014357557538701378753423037285649046608 +4668877807427263578702242652781568611601647436182495270690516771196062145719412914253392449657363828299691513193636571040444936 +6886803557429783411232920271165325523458164084362409904470754437138270784982746722601116409545759719688367256037780110379647023 +2300356518881462544550334413932731049376257074071686920169237332025867743966470152553906187385400688773328478685498436856277618 +3440412356699057087611095963196908710039931495043446625996426055839433344899946522222609944936452224313579344290272586318016202 +8993953982184479368753437594218699447584551919428710623167603425460525558734552005471227418138809891695708393142992004433396157 +1641364290874900167201473644758505077028954784718448987507776152112958786620272578580374033281627136838108776465262593788494546 +7335791534524477898748563122556395894974284747735359153516999217809354333772921832279669823385048414976444462237355621384907691 +2772845418405555209223672772048928810050444241654554863085813477997024445507615114437302354148041362541497074784257678291599777 +7178588655224708624898385615582303156928858845044700179599109039474166324676359828675085010955238472718587302252391630153958817 +7714350463980300266933000010667914572625520828229727411680133017980270989103849220980928606661613727680746076907445089175447948 +8959804988970943750240542329725780454835670277149205665534383835852603494258686925800889072070906952377561912949333090450610435 +9127584427822125708186685340454898072189100248623017561567872674357247419856515640720894591586135918230623264124957308938825903 +8565660274882658423962595807578322116479785809616786494557353542161430392650520437776730973491703217016387298185714704526578560 +9055899627055835280579204790473134990779700561942294557075688923514759353053304752653692669704340622777026507467115119994712711 +6401990429555126633982558874714554382951718476795179895512968275571839503381782370343773457772170386412634133151122279458473043 +0293309378136412704813380987793756861154395548470319616928895806731980671468030021029452883570696897386186564749939824907170573 +1417717547255789991707004411151265689787923836241613415526707758612724058279277154586299713815047231145490533591839466123670638 +8468716387126244026336620863493031193083664955579994896168486675960587844522828676215258682659462698160621004514607084416783102 +1449640886898263771104309544234611139764719954279503593908340282487665393419611852384397377672021450701130079538845214864162053 +8048887383017701836654298226594900075026630240187365178333016065699848058831083230977644940244574444820285738121188435273257549 +7734838976170306869973081121181660448148530318134255364556117593964696552501147168845028313551613495459270535042160362667658266 +1546639029308273692114824601121738207436726090786353640928151613146720401120659236951025944213817893334993036938357931276755092 +3794363877794105698247622335925064622343924657500773158672876932260234127197098205417204884907008074223982761704004122962430390 +2332227732674259579606138447783556533770663844706548255581644643545209413054398521429945056259218551425238380035190336407512204 +2377559578117061565563176922758264522313042429970112963159461355099505172026291216364090521544160711259708331105724128544891227 +6832428920554567726830569490691102432470549042227057932067103955101428247157515420358182404777567376342028775513115385366928419 +4241634693879583625259038169292585038330173637743744110426172931636984875802849325805759410994995655688205543970012859415471525 +4435409455307673255925091284402380740900794676435449835586381572908519616958395334196299914509777009013685912584824599000928046 +3987500940366892833785739644668889848240740024168650731790637517228616861342434915208852629849188021280517196274460956387434019 +9025408507685643206458885320228075047871187183125968258455419735129168110055303698970042275578099161589116560991845557669784646 +3366934559408606275260397226783303207383085083713447307195484607017198247596814686470103165356557218941037264416211493596219700 +9986983198296827970312097410216841561044999906047830934706495494099795173682393665158233220314878778675703926172446242051213291 +7327936388263057744394340969867975674336916032235104437222484250956772949755115175068343392125983148796915268153189466560134516 +4505129238068974615562203042384229709458199180125982048735704933556566236123385106774238252165050271569657572461570289000767894 +3255121682739504137653843468184029546420570010142268916658213975683507187608005044348538495082520951103601067607492368878206287 +4806852545996927533787451480123813559737763425204966730971472301392243492249231539770928832609537982396509758149828504617903683 +3852759316339548366895421554002413138488562983328873969801625973328318967409411308340716647240522981188392227872470754156065273 +9712297766053304902554018245083167928441148337025924749085910105658941917341095233228980927774840543348243226033787592995988515 +5229452832882947701986160360761878773430032426431930521890318903440689202842760168592576826646125537090631410703061420763990796 +9492775501530308620829606600311339640855706549659904872171618766544922435708097161505981598338090549562866490651446857290681656 +1678808597459616937537502211249586561665053229525645425138896011542471614595178170509581277828163045626690556829964393718215591 +7053324203154477437019031580603162546890080238913915157646559489429803976541625693989468806549155901365763128179885048681463067 +8903137465669354155616857417689085085205927330249024040964205952230818210265737328326102255377329508350857922236822007786665553 +2734904576394968648887628638128862963608976988414917192020797115239688653336018428937106985737003652208275177709699892675605393 +2387808386702882349280874532643852585858561738093225119093657885192947905905747402618285721157837466909047447734685099503267111 +4222155360968520550560237616569205752212233789555461181069785768415048417477169723562149137809030351670058489971512620085540222 +4298243051299268952240909211177826836030583513734437502197104884602882464340255753900393068054947046141922743129752936688868680 +1061375462364377985228292590232948700810225069864185759076897910654198361668411325978825417457852278989472721657536588031658991 +3940469338205826791620626804680412258933163753767218044848985297248760060861868407223928680948167632232230076213403901155993516 +4300489987649264460199719443525675282990880539122997731272161031062934060033770185574543151609533413639501738216215062848675971 +2177118188977585453528133388279985990921229278769939253605657684059131396416486258985713207176129279519783451725740599907936598 +5468202247459866994978249422059052436184697608003427416719037990135361500297493876720017894883453685240892163318994013086288251 +0775391987748089844439257445352687468561143051750745275017026626096588861151466861293340177217020485953364933966278607508804835 +1628141832107584076506487704333155316297016420320945282094351104594402285535278391969212402643994113779520387919087141691462578 +4568389047366302665905669107740203378971862242851210284195394536420153604497510004787197852478898999690840962496286253318550921 +5915870003274717803413556085627698745281531034825477926175968946204463905051355885851004345422507537344854894128229093755738123 +3106488353902440267975241920861283331388536648597182081274348906670754514090626662608509624737135193693712779860188833406087992 +7845555194352577902505815561879589158643398041086893321684441920278495085775044919835855368307051779503838399479822915036806084 +1432352323824143885623247262050454612927281487774810795510104248803405617391180050263987964438216016361064912733157137057008420 +8930414578064185485247400714821333921221435324501439994244285682760117301820424406392510602828280562393052749013520039103061655 +3173670884438194788166911165081056822054430147218290866655074231005149200684080014396687574505239932587314443974144267617007284 +1527450743968461168931869720925121234258369083166801029927251949358618661922498099466741377608784560458426370545741910023312926 +3234042028371733121222497619662259149254047701278276017559926645765011785573767542697475259020390524937117767929011666715323342 +4704564242794556338739787095791826301668604024597313082723259898622680965079684337190731792129157208207454560161440807617595099 +0586578102694447320030978034126807967366576180034644346378206991116172450484663474175770472979363179024738326934451423318607575 +6711140559239053730606360957739329179929007703740765446432207582934464258439308670322228705839599858462219965901802441668368441 +8135099406701400995240439771871466473527799329781080913941715241663698683730014850012873125772974693072458542177139084387995864 +6568179602365017697198231075678811557245910639538778108917663715366214082022315024791477373317009595760696873073681125441512291 +7907116539452577187506381898980888332568593365733396512784485641208492339371509014105839017197673863709466309379039960962534849 +4750829188061091962181509528044851805549096285740983094887975885868844208625379458016822827359147295255429793714493083753502809 +7458122759750053134279494636824383187336514214679539541415555708799139748870593845560799552363619897719534116667792275265497323 +5164505983084622666859401822913429103147582979952129213026773147211467590148787858747588041796556975884830210299871356341343674 +3017622046748766474496658478936400050960631692047289817971683454103138868538745678486924385845403439475522996310311537377432294 +4414433427221551082351019813726625491301604743637648323980649889708459225496111765515476857080238229389480546313915805440388154 +7128048414711368158025433805098117535633079017719775105234085215317610434688668866637527464933292899758107458405969861125706230 +3650863400245738680980130673926472854780296246906056457846071189797804208159343552552140320987399388241880735463419185645329289 +3725491818789741913366698889863185159632846810598378698135320769928269090296785595628838126516727088490704926873118733305049981 +1156989564178115063843077403158558596361694573121010738084003904783088187504126348616809589260761446819325657369704310082822306 +3039078554378698516122693013852039986103065748081512292985255853944982748397274910996825923066426171066912792454508578589756672 +1894849971820554276778681262205130604407309897326530536792905116831412406644096303796353324894146354102174288891383230945427806 +6519430441043631674771677332934701069341868125297788424647970576482950339536231483156528606063230103167532910086072700422834732 +5003294909621810744051481544451641763277370208048653102677095428677725528287642449117886584959778219864346164599848968697364087 +6751096317004399279893854072583230132666460132819591435600170059086840170914605351655306551416965997517899522960511150518237562 +5960953398400790636904166620422410033200069198453455057858306095484024517953196610901762809189095112424175853725058162186086849 +2378590573214575182954920757796313474094094261522041549359071498710995391605164191319908953433331128043052410209542459403183882 +6967512166626834760387377091334566417667045028443096775043889705526174743033818006933449908412813794430186357687977532151972724 +6459437209781961727829502811704779207375810741393525593812033931653583330049048561114949732748301604240502210577065589268424394 +5485945220569893838677587873427380906709988368671130490257845675526326075411511740051320570135507392241324306058959038656611327 +9867804575762976412063217986099462524791020332282964702449941371619543236717809914141667173375719858685004628965511347280443141 +2110798992334306839358051076527899985588107686694616660268331099137978416904137377280100977825535315137051402200610861241845328 +0363449860601942956031660094586702469467050513998615147930502263169980700379643465968570908871332692146680759419252634341789329 +4178175171299653546801324512595807182660716928201014996433033979566075147588628538125142107342090640143892070802843256418010103 +0456138779092213905930697158012877976499848878257289818896704580464918962127445564050766970246810568260512090191166877529968532 +5482828669997339487166576516726609979104834674845622909875424783106655102257236764736990686408975204793086071837965911886835119 +0267535380044835507754251971519271244598217325940244403179333727723642934577686643739339224195755654907218801997476469074206459 +0065138464529181013506268966674037435955704351830301533242369638335168171139259359713958529701206344946001165594755646272160394 +9247709545287060822576712084519151900147438436096859182537437687606226004692040127414633767616557761740535046385163645108196795 +3057714321815816843173779617852754091940306380163712243160807705160943819438940855547343479788687898931661575785126108832716960 +9533193667848378687611829528479562728893899352573352696025950207316753012731502558223567081292045437196069989946207141541601008 +2746003049129497733856902406986044430147897422219852413496236136281848127325893646312380454532608491203709112537348345922832907 +5956088319437370932773128721138395583820924421370173711672367829016504396575639303752597289231640106298414733655039838766441635 +6180360982212734271044173784836239476693843516863507994881855803639383910953492384867775507310386199169825014966152355545690080 +3474612962133776522839849256217894365224998226219581308883384099107152737580490415337430321380267679764659802341675603080689884 +8385214883553973286697705130178868327520836907858212929553123995458539418552809580075802670275820221517432874193060009661810485 +7635870550202301972338809565930908660230248958179738819097744222123891965883613360999839391218647211336772299291193850385713932 +0223196991899819000674224598203238140866873922543961390070945675722542943959098310960399662895718004978803705167927661114684329 +1353039351866220611077365279049149758286033094828313328435837478853295648033377121231092341833142961745552118705722131187887027 +0630837254454720024736805497864487627874608261900388501662821043624883721807437900259240409899471051510920617085099514167842832 +1988240186121860008928979530309334258534723646976027619874403865209331775508112215956780097468860667994721263707215073620406836 +6148969112241707992551262221756342003961909547272415999992255159101022681539848421608837594935060720460495066842999521680449846 +3457737254497423194650056810264127963010085423288394333781451846227050135758357730754076741230565869445804248143832527389323252 +9790762908682747953438433738621860423268309500095630988326150834401786630708761812852217486714179905608190957461442085387864531 +1522476382145618120242675473759696978079648715587754580423057134915914494338656294472760447616089310947302406703031809581038470 +0171535968526973835184507085581755238300981407341777890096801351412273999921818828858705625413157216884807494772810429053662454 +8619996360469711195123911341155232797961140155492488752604043466908848244534955186766472286479836154777737654804742176595782417 +0442287907416054215425872961111123258314536732740500399960690027024024360050777203631279476105577959878524421999307563248792796 +5348313098484066077132466864776966488466415145151104355092278471837179740560502130988667962911672913331290100002597980514807384 +6973148695771873711809347394667364106588642004523762177909283034839940053425839430616152779752491636974036365528618662576277264 +7738896628379276240988497456223829719984420682404133841162697625994660783140799400756439398245883415372300490612168611754778835 +2968155785628016494964773780830380372616577140688533547628672148913272007307142811840952334174750520152544790354169079912774158 +6160810488767205184519228782587106677087830138711937806611950216229418417002136640356986986424817853470899640679854298647927259 +7389645808720792658628388654675620007991152874617887911936239398213102595897949513877377839509375811277492184373982237380291082 +1922598786894089615133138929724905768857077536113336665514173213408958842847812824030198485744051056380839435865339196791176969 +2136038443877769241444454895223211664298720250012371885167260344691881420934862218782035849576910341176642258076960704001222187 +3619101830106431706477720342582912973276053324213930842322321478885278765764844239473581443510550046154371836075105045519367692 +6632620878175338782391713943229145276164648915620343082760668528508249819030769842115080099804055533615175586509178038716739002 +6375633941128896351976814553047937414266141967344986676911841486068926852092747499580940682140408121435982416523093436110154753 +3222139045759943847229548540511800241253979460730316619733573541964096618975921352811299052448712030457862855059140594007137487 +5542934473211875603591035637674955693033630753758706551752052733350020451305012112157194134893565133530552290159364523539238466 +9616750069420348886409611437053219761816707838644591613686536212537661191394262393089971240329521480448880783267163369576343597 +7663178436164222857814082876400941903973450706396969842174499622842764650335956819817450043712545807776915977745377365681672284 +2935635454175721831517097628434415466056776136240049950596242823574086540015840252558304179235526092054789562977490616481745276 +2672225222323108279006574534254983148801337991382313626787322270855974712894468368216489102586460739019744587910470428744488347 +4456074219727400675373619105257416175699861456100136151468602175897841754789156021282927447724373220233266891662271346817093855 +1945491471112632720796456775261728821202881302996527116048267476118823402662922873934255261349533307722895013671219044843545536 +3793064262773487395195026532854447589979023796935998929027789654646824047287035198327952786992674028615601422606183219879793442 +1630579923384492024946757118026605898057464509351571155868933384841116903091820066294965297665314366667361460313869589402652652 +5439801899827848778232404947415524928188533947410081530624338414230329436826968163987544645598294181482211511249572440955572589 +9020302797433585342712570197932059030880158484733343798115491391574065623327717092645488738835390428206014044345059915068656197 +6778633319862050630189473515487955936852231827578947650626369925629640725543661083890077620427798703103241551925751730707081567 +2865799458475868282589650599779702798567688820803000453419899694057649460056145012638522245118862822941880894081107383763766480 +9848357801406244621888791600769552120182014301160477310095737436308083268418063998275870777119923015945538580745005505737103338 +7945415897343252007659617183211493332054610541474762203740026194193724624943127621999433384183901217893204226254420212426063638 +7076158492133875552332779907935346207685056687613427451355599366014714317865416656614209291512293922523145859007589727797381122 +9946333250403836274494116934757202951674758307883028448572093526467181950556672615580772027423711017988838817957391185266051907 +2671612744740733351071730789204383122286756475831821553120581637533315869945628262846596800538216358815764391197830622296989405 +6992508792590965425974849365498446258624365117468171476061738700582645427331625361075430086525639161565604855405377972628830548 +1767000101633223839049666007882398066097691088797110243675124836228262533287561681535331091157763899063838214576189418367103407 +6457110670443987985620843280548984156731527065461755656723700576735027138342545097213504695717743645364185206786138748271882473 +1596890724976137980689991632152444686110535638170408866300049744279882252913535065665891060859035882361321345023887058337830307 +3373623078064203672049256820541947607511438489025503639693977760706696248117065715718091918113311545970967574977708709494235026 +3986923221612406956343410179388036231285096894778891156497956823154793926342464675477516471934158222112337889492119504249964378 +8383359661494890362436075780531143970408529612575647080037096675346333712031003814298042719987791032534165077846901212886629083 +1031714454907840175775311491859630739967098843327841882994334085317927475102526174618259645090436553276509976956833748682136561 +9064443423702201811261101290439334315411997283217810869669829733471939988609585312109817796812433049198581074521140984709783531 +1643172980645376942250431938106354165389537074459078140489244293256765030797030616074547245248075610258161012282504441637098927 +1963449744475973480042625489273145165780544076999525577661332442838969621507452441516783367746336168024894911875794707697541031 +7478540879501049129571343735300770536436533927260315165717247241849652331693508424305277324109605811185754598081962423629189840 +6683599827684293037220931133813347833193703737280727037200969748489983084498274135581120006138983726589125846483814619896382039 +1059986341689241962791022654106008362005402932907446471177268388894146241093638783546170660638544503679877547754823916693779241 +6066567318288101759879455792419222471083652734422111018558539534478864130071102091839803023165372816119391124984955696131849946 +6509143418752724874347225367749147359005862337831645120532349014908613153135844609402649987007336422813208143055617331460906483 +3354213199998376189921305928077126664201462525396049040175801721488556837349266500704234038852235700461088952452452884233498199 +4256488814582093977539133655050527294580364706253926894156976648375809383013472871520377935537449640402956782630405195205364082 +4477841383190587477839953144802218475017581377483593956131465077901385252056389226406917609350544701283827958845981175691165035 +9675317500791142255855936241556896374576355116352110763482014692288089584642987642868701466081788634493767773533747361856594926 +1789275548140948127853887574961649444778475578058016075475638253177323030087374164983465707068923303558558151943477905316462894 +6521653020185636533316557444077296865023590390333185648729621716717902230175881569862122737390334951189041533339830180227274093 +2241738740799072036341573074248811436322223477169424669409308388622966605065590062245566692647119962939155999944641570918974529 +1928105019305194521974392400885274260434232105024551451106931113484210411868649822559924165796213171984543031754090678126105970 +7223691850432923916423525460668413682740557862272946646859452084367199412001020057846809103738751744440123325432522452656213927 +3644451389904659220831784890291126628998096321970481352017638587386823902603865110497499248782547323024785254006989249949637036 +2368125490914853885015667449603549765164883918157991257417554348691174190395500642639268158971914968315411160008374937535704060 +2009275459811984599922767470947284560232970282990047819179940733002227635796160576632463265974978503386125557611307429312063703 +5284668210801338637743489600143584813073507148578726973558859500688227447435508050513188287747750741584385175252456997688110085 +3073679804770516059033436419747376347125944073396261678398209865762812539120718854779696622769273119321506920635555831381282691 +7090364017219499874566644997354338979508197792775841533060988865177458368657219876491035282278982441665265219245626731000939486 +0475155794230569303711742143247875808031690475413279208626652350784369479556492756708347931041817323940692567675398118277710202 +5987347394026084150071478790424131834854554229261815024383846088465079695373324503305612702444122591068763896541604539174335033 +4436269510455092220918525983424623113381645210225340601560482106077382634781965165780342556554085651205866195116920957396275838 +3995168889104032757269451494854524514321628575817645123299310077893411388042092201792884422861886303896708749182469953549119697 +4535001242830034769362884028681925364864771844433940737375296944979919237869503095436572944940055711115231301447509402025904715 +1800925216578304292056694261819970660613390371844516635237968892926502845404165792819051199273212724458326926182758338001776015 +5370775490948607047730587515100674887605918594533934598837391246334643161422435117919064366778561014958058716726561922586618810 +4628995498127197780734498354535034114170874670064085395510892816542000534421254922356138052258209965701576585535292854686090278 +3177671938008077658853731628156335766651218454783739782004804667103681002183460703859644141290605438413899770795832449230476552 +3025608055188815266778993615750176656164484757488579062995887277096944086284232725973634996318024169760316516668522315338538984 +6602875499695059755336245084768806035216324561236127708770615233130804510875618781547430556541215471796577953669434492812681721 +7882550875891690926234268000498082339514970368440072348061964280285302475222074394484586936518612822244399239424272459493452968 +5181563270479719492880854609815755784042683606603220498202601700410969538595041484423412424024708099469125521786037119075251369 +1248580037503075406711823500465872772309344548541150892813671393177549861436277865197182621221456420646870403987260426109411933 +8643731351640151069976036541617610531794845092323107755413601880551863223421905943669090586959614825141812500194544006658912658 +9742463384500880801343550699245637258131793581488832729126778269174701627529245091838528797596704702724365423027345604418154777 +1491976930735419986039574314558277854162840810698202905470921161004472351694186257157283632042119485754805929863758038770286999 +8776957710513013252382046879914405882388509080000643024892306629178763000752942096488188622280178608686752194263930907107345039 +7700223390484500013939160057722983340948636861897585123716556883603506625380447610584051517968188462287828774879778811063488262 +2795184513732482674790613434841545867779716565739406351364662664736206685645328526381352909903916053024580749353932896630696063 +4740510819037498201511535054658680017511924766740460699058893969064394902765714052038187304179100787141754044221407272788215435 +0593548885409678785621622325583756109305462458590848222639139506641697602853829884986982801690179238753218240401694408881886033 +7410128478642788707381845999570686789659413433178313527394929430353749262597920022866973734073910557598880611659693295671936482 +4050137980096982959463694663632299864309583041246448456720658864445243964035849113054975666938514766051492807225377478267946248 +9230953430893702870077668677812927912187053690817849571057339797969486866950088035191626753948056471063994773880460982770051756 +3412034824969056166032815703553100561028200933063775986905919463873509475321843375830242381162225467967586471609115452917543492 +2276619135566337663226716925555717587888015126203959605452287702197704113342129180746061351411545746680564903503675372305630554 +0286134594910134037086879568380122070111392862199290853114304309332761209262076794099989915249949904726721530928297666077629849 +4661032688517145326760870540723245614758880946406315180541283633369882854109153204199703802230512566561461752474068428161370873 +9985206963338598229560272298952476210547736996017303976951734650959079279082066159408637306110242916656383381032701493580216738 +5913923322753424774114001206375448829014685918194637698592895629614872898525330558761701771138709572823367430917528581806962699 +9424883011486160624035588850742276732199499002202807230209412116053419259700106808447590894864084149744926170960405759460241951 +0111713605391910548623870259046900302961074850779853822097709685279966809633918351283533571183917553534326378996150978977861347 +1050239621857569309671883851470643478606629045329299864518009651449122046063366124913574153087650803586812245327770871382994089 +7338464051366649209011968493386794592783056535912533194525947811637756396164788969037121048254365052840678648300844862166030403 +8244368111563457237764184604100489524090599993135317160344204589670504422777317128963386304474615794833181756194674508984426359 +6271679168658661069189900053139794326069244916803991726466041535821018909664293827288937825025439753702650278433431356713529954 +5346852690092871593686894605405997962260976572140748476453131018042391268683407218753909113662935434807662980531252367452071209 +0705634605741863631683627483540085543041836023190103343436641588904884705166081263630037435630150915106908441737355700766586129 +2154922713467232816471312647563953292047715226865590918846184574044421063130666383320555928376617359704955430293171097080231031 +6735370005980738361139146789749484206639522950848455336267649778629028940168072161013843975973729190308856690607301961044761977 +9897723534911105404785432577277586147477026587182509778621368216939584671433875920453602299697242616307976245807249371547062253 +3864808728722705732544842322660134226278752025136368637744919793216576700939583871247088670783610602247097814787263304145508390 +6389663947104499816842871432275844541634180937582381422116532840275365272420061136044394514594368541860114545782560162829849217 +6461508204689611076377900958964324912439197085219708917353896453831247651446957118893020342996847989755084869372230374673491989 +8433851389135866749479827490565035784654953912616746305746664585754447284795963325019971006496729330088735987747297399102902166 +1771497470275815103247219843409878702558703790756432271114114625912471360708464566558219993340028146304118900320024587821960404 +5976617030473732204037553918438169303047012033263099608437162902327180494837554619833899125897762143660304278719895133912883606 +7761343244919347585259429796353568917054332343654064758967248953925035366120514799510920191615767532119958355777106600079597516 +3095394058128591553990850297147880385815415033833458376807751479083131186229720635268920776120415936997418185983635572280182597 +6753076258298533536576760943150454522971260918131318703703785217592559654671733288977408306810194844570204346184368392179063403 +6769837756043399718259982003405678775173052005969429887545845745194588303371446514765027612890020128811539282517812340583382742 +1145447512453860943574015365970108349103036202936884373965378227317332374230149559079576915215507294569467043944303098203606050 +8331961563901986606916925958427127071602998579210529763507751756811409645017688889813292902535048662887095342609935463489041490 +9736668802244229960622339427799905563413901438749725163782080129777353814116461004513305940715905434522181472880934497720795747 +8089231465924779558459413345521275018800849869822143818873965081853887737597561843190118386215028657051186337416427295963183576 +5419828194392698210106536176184087551774670922454148544855468554475299499312061306599825835773673050247207685913286844665654322 +2029050617375681673334265164512497368276347055093268045529145666800867914194334235145959975127160210970603920222624846997715913 +3338289728310358326423356618856438897910927180875153967318427145237491006914876971227986916804404786999082347353908985698677388 +7003592673799641258705037230618030712653838166741113566125031462959628644295056269826492276245790120809269461415827782914919523 +7933808111352753578305022191116925704616734794703884497922451694274860316351759525772713769181944674741861315056661705956786856 +8984597543198443392303548683602615735750098545443867287028649374064105213301110278127297110019939173598045760404284726471910865 +7750264118815211285409646722851047280505065647360902845736489925465867330208651476223128001763893620074546763783343802324668030 +7948487261322735741590287466404743224701011374099821413313798593806289098955840277566381979282309078179393391191764372912784048 +2031963937573607333606298955675833689352959686316425308916957187087489102352532079680910868716368077096336327451835608150057007 +8744303746009770077282182559273953960324245388355803375849588763883535017206175431068707914836359928021715134879810455899706000 +5908433402194706392948327209167569206279605516036898586828659659699407482487006764800996251885333845684403771593097852947347762 +3484375963559500648211146100726353736952008101895174614070329067925003079219669004928157370633196400836866091467437872364498774 +2081484205640426298611707106538905461885148824043568864528394387116332857339440668358439402474410629912663441714724196706517933 +3644343088909312558175950443352757850056547896271926536743829402725583986356025290388997593791573353364649376090870755738159871 +3017618967565343354488657372318982952747583634309941675726567297969538505603943497976312767045820564276791044673865996984930840 +6755211147212352533715490737391598930938409102322563339784308800982635726297552669891177376792489264858203303571448544969284885 +7207413969560628720009738274631537646543552986672220301640499255496422838738773705028096119936179105477192211718065255929107570 +3952347918324244189239309810051132796520311457058379135751168560732193370931728585607246787585935133034431471433154073599431184 +5948122133173804973271249039138486246462418117000640467153032385374427588683813496957187641107568040808210363510174360369854945 +6331834561201934486343354906308493614367011264274134644391474781936706762560771237417922164625963715633624786284367782142544668 +7058988605827353891060874777352863740110376065711990117582066617633614262447752203022541270469488891576938820572417323582324874 +7650494373012942929901692837674976301695625545739424778268284160337186164992467580080040539595408395037645199431241659430435207 +8656919176134694488026966726643058582704520649434961668466021566505602068958240292321141423966866142697100141650705135865554164 +6074242737126300341900975693876422564450176547153341379638414445501488034596312318816964962988094532520846350286682968373824742 +4534503855120241805255426868718961329975641710164649957599487128347011734954984504486944932371682996864451326051197976215900771 +6630007989276431575068445405328391722701879448701430734576747641331918162377214794008905979133776103351215776077151862759310804 +6329412106193202180915032816114091690804553056071102324935636935651606678882718350090015499101515918794964067433252126478982521 +1507488089191785086302341745012119227252927110062837567351191646939013086128950794430586208439605756178768284242079693463131834 +5134585587442274519690540303774472196006421738851673713532054649167344075465437753885506375501473079004259777595528824429150142 +0167920777588082886922742091652852786107461336681294679615598036369318540905949328746327769828031291675250740935570893774610363 +0038129096989223899832722064059115068115632289006675498997130267266081648211647293457692956590546845542631469315045748487379716 +2520900382745986244596184110700770194581607346454171538723932383294827821673322365898338493511029865949821494463508742106545267 +0565722884026288308442559359348206336146522003909225136977382087745807490609842307769964489536237904153503877870475885643050177 +2879720936866676112852941496983745298612873646677215750883429056007867869583163799946103524454641087004812983861736126233920563 +2682472400392268500328620582825275316785330033129835929951831594780666869418979561874633816166907302039950903765570878767521214 +6000673278648346135978034734234778737168645117288730828023574501322757674622590092065551189918194137651428146720102471591456653 +5327338501961495043879408610895757013526179026139024561532644668711391863557108491473051168562266679950416482721536337117909482 +5138678303997976985493577927432562758408369467781462054517017307284536794110651849922852301352432225552398279721808418546811336 +5942090618633537593070786933293193872947689557210874049534803895058142797994756021217906395785182382696435144831868446840701054 +0805866230785777399683174946303367253262058687100298157860323746012789121984620953083069679370163347105957163924550067110548159 +4041789012854867563385153833627178486269852350526357948901776390770563029842109776450416321004402898078350462398525384367774030 +2665510818821145216742821558285899523213916502258403631925403804385199376750878427488901928524046342897043307449003184422505729 +2048478041171475190266068119919204384149581828382571880806118238802367751477551342088588974943077841775978105590373879036044712 +2582550167003327483543334770578776146465326561660371559249486818965320307377862762238777770099168908851119806663382875070424505 +5444680706893164832113072218288281376031594555136022935125966599054561592735752631698479935094668438169179747421550309847079353 +7876386229779167984882703960774028877296085268097204654180082218579776041301150186998163564389835397054336008319799363495236122 +3595623959964323543031266473552625582982574960453793645874949571464306599166850005238654688725079784506108930071723727021644099 +9904805562100105780931661247162991156577135587825115134266116050943890998432158388576995701892214120245792294717827415173723116 +3198902109416772023345787207073949684308452839612518670096056433197360112721221131290963458888501686457236376124484894557704239 +1439923839664744053843985184599889045003833780920311682293838839176055898856807867213644378338290067096258257324765963896543835 +5700918940980369141639579109635400284102047501766736959067637462699617150713985354772825179670276479720622682613542577452843978 +5832673606644924997743952540484980626339060224968813632527031664879549813855034184500405400404100621140495713672255224298990414 +7192889419886464319914820529555240869091567523179106161721958129392253172606561099507740238957725006168387598704764060094854671 +3309479510962185342022955705280901753250343720767822070179996628541199963151640899372621594086084748660799272108092321615608962 +4880909425016446720046143938905942262706584339160340379312192206890240470343647456683169242925461208146677458242299921836024115 +9112782389287307875231265914118831295892615090298799256818943475243201711378213965480178556028607894090008691361256922857338305 +7878997196220126636517126620736341433150726102479409458917297307599211520723041036260833938983460239182766740060701211352201191 +8683094774314359941970056128495804368612258860699432526379799515947600345795062224586289908023772808639146020665057226162935485 +9660235891943541046373003698737146580202165968722595524291168751596799140752064994627768069683240083731174671310320210501136516 +3952299801633659008575843631974719807086212606583728034228268635353770341290150958327244666611228164923899770204672751927925499 +1681865396625682674685451758504759903079794185779114689381923935911799262023419094458049787998569136087163049641052695527707434 +7051981372339577512946597699315700689964039969082898958542940125278978182609047860935808672479205043611404976983579300374284797 +9494276685604001130980581987978951663125638076896908522150813094023439651488500315910058707670482621722763754582892271598798239 +2297480620143752383199993430862475021936852109466853279971476608874530008450195147642064323768140191491557557914372058492369456 +3210874548598055876505562103390094444210433165591830328725710043504607452200463576289609058990598342445857989436858490154233543 +8132507672542987199374049904308113523835831443354290371614453927015873910762212637250354565826616538395197156337073925141693484 +6828640682510305462478950222772217556425645114511993512248969567524785308290443499084010706135805334321921588360262285590883409 +2594395457778771868965633838538089838472114546447151674569430202509411063873470113459762471547280714167783037132473173267526307 +1601839698883476534179701757004074668031433379390884724016658551011492507080285489891017007383477491584644025888213433125759305 +7523532561560703539946099285117454732843364985652611667389407913314960693543811095755855340427962780551780192610470189512825524 +0348247544916136977309530119407469259627024645454634138436438519986158560867875479423449700625816301245511994796229708771223160 +3917296084877555076938742844381232454610347414176454495292235478841999037736204668770470759408859717947445502249268193464913498 +9942815514474689741504492308012003642347161752400872994567873248383495234578303317585084447735662421098893789187005435875125952 +7400181919019490047282686705057224403673442901199627215721287263528829739748034312624985655044437343710362758307470148179539661 +8008829134593470730958751998114225068506458584106597781009501241738039124829892755451932663527484529522803117480503191650035801 +3220127286757997144992978308357689318213671314902934383221575508818519767526663815112777913873690474000419949051472231014820267 +1399894109901863236068753449286869832068943284626949416147238270002784106062189067937811371979896380361397172738949505933506481 +4254902873234171699063619785819122642732788292824537159169433666069361612397840595910726633660384383938078177403483650975568259 +2209368179438159444309719519784098123505334554409147220881995692904711982790135929098654648473758048308424437091906340638974131 +8784927854050511928452505266509077489186830632833630361379070849447072741785156478899585449175370978549268898176795868437582718 +3231975833056135924558298656515294400028803078329218944207863428380011023399283981167261222531969361122301223500410734344217228 +0958925958615134583645559459986109673563012970906230471133763988818583110841341249599399387466204289301186261815920165156578985 +1991110694748693828675086932988793084291818416422390423728768580762059411945640551530823790024178761601499474873560699371792162 +5361696020245404957765470178767621215479647553935020898244576640893571391546565492142211775010405579641149396961911772636411704 +5933641384834122776049822258585947581244135364908556513226296636775905866481756079367435635748095205281573764949264635941006411 +5041011491795017405240846374877784042033437774313006536528316953327476960247723830386371163515353865121506304361011426783525735 +5488997063517475914229301557438267886673740763412117613628897204796477231021684048588313466034859903879245472327441120566103502 +8530760385580859337137628786690355170400764087907594224462686544107056072485919968752489909254263413225447887894153248109175652 +6157903578937685041336967794345564722166007100607239958562466608483279980200607600350161886932405833906442195659498278360919430 +9828873150674702059652960737024831945705458855872859591267757719944168191278626072224244567244295799804168245158850647976515767 +6733614592966966657616990010769041211313964384427177615958783894640954404002730067938048093833284287697173874637222402450231077 +0815239310564450548270963278380227549898803833625140660969258768879529883774142739151346026790443492061196604908528916004363340 +4528891650268490704421594911777184311417838640306057034638291330148034732863406008674812263692977851302665645685305101437279625 +3233475352967519524983898638596372885741145463145393518228460271054338502613983364910024616107499752355701791207046590492672193 +1607766030654760272868975035371747266369258074306005268123601506826295532939956628851683606599198929232644016608548496393286833 +0164998823596400554560038697981327950512161089142533974519294883428101099674224630562022297926592367086936345609705271926077768 +7913025882622995728679690258052758137385077334095212932668418477051379462440145509778953697224292738907994604706710137743620276 +5951558921066718365814901585239666946472249768608440652585316569506390348051275814890810652375791805564478189249435436637972574 +9602716095164159166038408316037910403855288603710934148506742938423062837375851102966449162297207365844156933403348477083451353 +4627498098319599205600533412615626896664838788638148524473652142820739172777284703948483540298530565794965460580139030303425684 +1565284739336025065162908150482205331120001945515224141600855715524039864305366369748820024483450351626491054274468986093516231 +5340418952181086209554963565801571626818882174630725995663438985529129315572417340527118943747399687973820656682726051844769221 +4320926831481382140838060220194555123790396897025705923345941235597539508677805522059012930976097312217637965288298799051778479 +0908830534451543414307140854781014820685341080782566962124152921948070095677549807088847640541631088209567823713325266131951427 +6110373879023562027503964302970937405830359276112650695138428936697834271328145752116000081078410010751317037848623862138058557 +8822703971093401247422493922044598594234342733457558225718617586744418222085876701060696882821515543129744401944915912235798465 +6594559520819849307838207390295967732653907650649027886332594421647601798566341345946992510422578734064164433073111139225071621 +6354737391657731503659719583497860062575481051328877369003533126272128322177145084735337788672157007937391387995919117571678161 +6945608633872227095639320829071216249131553642307083427955811490415705093929385003029105106248578633207939138356220965510578956 +8875904572434922005652897358604531384916362396100014485333372047722658292461442588088931849935109173016445065149146280103138497 +6344250852997852406467930596512109372720069308708153172074632971208643949842907827125891877283714403480777088075917332714430078 +2141088112696908575603884902935424862191062652660730968192553423838776572288261313561512545353202622522229101786236940604077536 +5420581455921312988453042096323962394005097705232943223301112814607176607418563604729931333417412031473023307632699354685035657 +4904931609893963364651740622632458671635349731291306084872673976452365611310080979515796567705432384020374827307261624382593308 +4292701172515433578998040448234672894273087795215109333668233787741983605782088266764474905796649724673659270602807201042559641 +0405892711593975173584006410022813365106426354541842020523033260675160988528765885253376470412464249071597201808795790920249321 +2969506372363457771265917606965665281965775472879910780113511573805868748553670687571273946389007257215130433994049155446244186 +9422294818781445710760638755747391659377594672815933259737267891227254986222671631563563954141355946614955207418080860505944166 +0087286626782644309557643177400304492693387991339358406695023215774476540471075851900070969889709559625262772196723374356067617 +5897279697608188221125456894430238474410213921346239465846675042270793388259552491229704041982502727376305057500585797419096572 +9629126678093091084216993559586337141715599917690951379809444249525710962700663884823083705316885810747054287541385125785199583 +7595346119985543253389563272070967517963948640714499895302628870263490256644647992172402145011433583359949875045014139097383990 +7523156213061115787711878716816595972617134587220608008483487762219572353605120083032766155082751752855610567994430397014253895 +3701919959515768374982789437891830642750960645657994589236832995980797844253496076657711645006752860088626834982056251575040225 +0979950855271929320767873941275718754278787090305240807928650242101643623835476811933436063479233606315724201732634520101994785 +8113983065740845934933526321338302277547952641828910682131706421618856976337728051411744765715349602385454916896966865817502458 +3541043147303624444017398708391616664010004834339935228226187248791543655458437287589626370079460316930365464946671005586907844 +6467142906481062672351612846421358624595986748030105764102127849419935691001942740215832120384833855662335231988116688960608695 +2788530799703761722379561161257911074841314957231380008835616333860526518888348729384930186986891972631716493196919384541563044 +9960151488910951179842583784111062968107086150565586865326760613018375413860020781154310137687104793480739369654379775885629180 +5757125618091793590015555342485911625792045336449768530669665131967481618933921507464033442861020327590807013639300157233850794 +0108561117599044904729443235325450727345789731582292014742872599813512719247096059598721914318005872025916402947088829272312140 +0248866797650825595958204057508507830807476195084664283762933417519142347075582529326241506715718869000199121666869916905668032 +8723343677864688450245361288384636252992454056026441166941292835836423200070814147349431308408145782824860695919605646719597656 +4679974361161727145100641660878503281476844268161393833161261969965276371391589006952925603728082872969549646978882403423923010 +1471039994683623569789873331804012722815466719684852070254837711074357511238693361592620597284807521864906755820794891745584020 +0746324287724173135220206601559184495263174825463472438325932765982783597938536871848298666533763661034332204209283999095134727 +8847622479138729829083277482843135313544249094572861179333595590622772221061675396437819003622647477864448706978670376179646671 +2102601933270120920559135994545418775337484717153198432612404295706149728016561442479718981277683958181059597372600330224769132 +1259053282379608711992970881977405077810774056106800273103144031624467658496517236458989974966732962924668833215016485379965273 +8337096778081104941132624055977594869352911917306275484469902456879306916808789684303864335821138065913931420528939044385046271 +4299368276599609244497481316123376963837008957992736164808101728168540138407838646916364855310221850303575133697274394091619868 +2713682765289699129061431150379322274699877403663503040025037596762253721290401208216082436415144118858632156119324419976312546 +2881609371673910343032291044465003849815028985591391451188131927898325558643746240832264615980161723347980750483187867525894824 +1267916226042826987257806754004440337824069605413162886825139485042850988829975658205470396562194892003670768255291562747373670 +9234220033570807048609309912547070817083287541492891357818324994480536846132188820055694019487542207004276192292369053562016777 +0261127578143197454905032632100124793899615216318941692486325853609952184783830755458121912666549923939667928989436426770649961 +5323045993412285553048451071174802746049476412795340271320920389475261483664749025686760199917517732942380222819936015551403774 +9891352074356975839408670998973900071675970495889358565662068888386887973659152279951010618083877084169404988201568384833942255 +3238050166229051707803832286590198074734858215255713288528844554284335600396367554775218985201346513397387400209397068796825742 +5728107211476180762511967972657147473012924183544027968479506893336809289253531374220504641466496667214723927677602095165464315 +9011192930379184487659969643233786997863887173759888022579642343995986799605176120033884333316312893660726740709366636155465721 +8537171701432226276253255188614622965933151059377811653184802609155191205285178705789524447418786231655055288040171432169049130 +2707936970837574068569629371931407362409248401030806380356720552870356587923391333211272591561747393382939386843280495661244314 +1175509422804438381892148322240590701277543376729396595323849510597370466005945680426336918977914561211300792916203174309654171 +4042805192423544654158411198004453353597731647696727421911837745446359133507547657475484034048732290037678198364742205283336338 +9337695707833044354501853716373623356235483326284612569311911355595616206369186001725088755459930335959640295675396477063274478 +5056639209721879485672408789757891131387617511313947970994081160678454602320697412448491839531065552976208944273555314142041880 +4822378866853898824086257862623541730929040197220702007762078231078349736989651940081080724932828161588263934616036801731107678 +7042616549110273896023934480613769270956663187245804271172698934235574549161064775406631472230169390486643998381840493803484843 +4432695989367708020512810012276065008453086786966931783819054126557051679306430656639833606398733688366162720200084451796967553 +5350105131227057106985599834342048762794186401151883770272342638155104883645396726235498798575148882718582404327490926780130044 +5764192533929612390344710894456047465905143834205988681215996081613514814903908502795919703880401811242594826105306055481784823 +3081736917550671392293064759141270028494719167332097201455667940386826052104961288882027240309372922868375595753000121957263246 +4989894866974583276238356566258521642610551259726323252743986766553600472233437943119765905133915493843897768279428421555764861 +6801273161353076961494549671635819940275013580673219344227796480147357333556424794612960445706828140206119824948840749240399787 +1573785648039077385334262023034272384935398204989144292167011651539471971703585283557377107933951650497678725581455181207346698 +4874698339219730333194095651462069186623435369684882832433815952523247766685863473588031370751632910706819521148264821690235129 +1745856207579350369947762735836875634021204626000569901414064309006844235664625468595115296405455194740314530759379861609679354 +1263339035301388606388533163722497011371588322482095769750979879436887231718408664779720990691297703089437248834586600555339842 +6251850491772376275294393458209471528965471290147918965218220499155230361216164967212789860144972751525815583122397986208096706 +3484214175754929920259118693783510869997218078047468018854269713219002531566539459352397848678904622285083238008575450746485192 +5035485477939887603964624838580376893177633857009639663967781579876337080786152281286836780643711497334038142643551235377859312 +5770990490009748551131841465407612077195558195928231585911499648608018583508169531801015305515399706824738442071885239921251502 +5505962701249607762425096231287734763141665342862391398728642501706701840207749211087529147723821274132607543767155647569233366 +9531752996646776228330405381959650186056935054844849148234315573131250892996644098435195362019426286916985576819912113865830014 +1606555436659451432978242703284043412347220692073799660822918397493666695089541486316957331014765996271479614514099842437809535 +0289392478723841946498232349165923942701150815142311799644394965419544149980499852640646717021478951519649059011872310248139100 +5511032978947425845575588456445816057377640214105339473284188761415785249565799756251192934762389301520020016150949209737931857 +8405804800314504366659749363367231268121943183966514746661384830649579675620136873178912503421474419998605051121952540207826204 +7505457456876976664571641545260379929258562329503538400546169325488895767917670975295980264182906923671943997046060564087753548 +1274387949977098682446892314563863257816052408410571422114222854885606494860672338931736657657635104187063013087549529686451859 +5042755850832972022663213277248439000831269961224227718932272498745376713860758630276016079020855254056636945995345396441383249 +6344700532270890968181465829707309620564725384105716449055864407328703719779390283415257182087348958918979888739066384714906447 +2478502268884223599964876320836160710993832544375139942095572555604343446400261859797019349052899588226956407340248510969084412 +2858479499626608853379284122587510516464864248784114135053508629608620108109543831989223100900685873301894882329747241764687394 +3516073975240799285196283516667224035786957693305024409405170967493899593072209653378675045399225752043351688237909816464332488 +7045960591955279992811051225021192695063962021956994060992139199430008815112575669363055918094781304106258599283195179624447775 +8303548637737456436395221856298882133107004338700669993457647426042442520873552718504789607522324772936205923889075833787877983 +4528959091848677214793256254827374251508317477836642431769535470979890535339665561280184226951752824749258073794415152803073480 +1459998553825438645486213601892946519503789985188491129629213533576249295139024141113828276369687768834808863007813942900737363 +7827507025285847290454033855290419493832906414032145098230761761828468254431382777807611448800062745765313713732689219610038574 +3538625159483449646940178756166257692080479081424585343862543469052182432934925669284445293824171233112956439604122644220734705 +8884093066755275809492142335471176239144329953772128474695403479470145379846983260079920680227191033324315471442379828977213518 +8161874018859891048210600302227335518426279312266158906257386817733776024570411483770025016852705510503728654494429243801612182 +6746947553453295308964010256393213729923189009745905892535754926116629620973613924884980514744338995975237628963272677038960017 +2316979912101253225038374374358240155041996499533280966926264100171569427093110935120513047386276224147665309331079377719563345 +5252039218101191323619349210669314090591183299132043388783600602372822313158646436097466384432429894279310347011205483804258365 +8849078102378497326534646727789474466302897639756078400720546541976326397706257168948381071970398216623030217390426497155999023 +3271457271229858454657827770335612567349224212240695221407152662225083082143775459834013524692150107287815992741275927112028076 +6697268153473360409230969649314429281006253608637773637800129431557004336600687209561833649108523766841818697796052616713524406 +1264023927267188505910887476294511895228887727108862538027310305679469919872644028196540981088809214448552558389500274219570341 +2341820289505149961074855375132511787200836031120337305514546465652487797619606591322268017233923749079200060539286691098575276 +7095345917502068750136490067362248249806291619173145831611517423916491683475780750592775740226681145459237438975249038494918255 +5854207316691383141245954956380735287065353365324788816174156724268585661703068556010195412645633774843480772404581791184447848 +5983572806554767884384691619553163176913927916522767085440008009887977162986441335924089064521714048589756802500400756006764546 +8749074908864079527865640561432569820363365495585933467014689209329639785755338623694144042638210864919006966920756182400308068 +1488418928276481402385931315882264463640479342727038934158474806015143604448429971536086762337574137961555181782018130291223711 +7647631250366260770657650732694263292505761395977741171841175592881623563088242488954360090722844918014816525013438918815206919 +1155451667283610662459850201212152183408308503418856279359708202057227744768180593554527508439246428232801124311275672977637096 +3703563826897681685685127020250312031591544945988953353941140195645214477592978106315927871524898358121653801020581142274555566 +0502532594831295067422782714984502485832073390165137849670240378837997996971720348493993863606290066187249274244662460276318689 +0152755194697776938435286119441290637386200629628199446127743622173851669645911455169432495002024405765718643210153464143701889 +2562249212763374000591086405147643148598332714850654045728093130807693494090674026473297812238167578278207216049055667059931109 +1802942091998951751764726262310843905210614073350123667768202638105831131108058708767584753018377136585264570210097378501497188 +9619747156993809984322106521027559385156110152742577368251988283361064503255508785410345421538190771023025825991060075397692057 +7975279398046115700291300736456549463218759811012712333893946746580558137896237287869240420636891089058025806023431754219781359 +4988421426405962855719392598321200928497349040850013918965781094541442225330283536616202634976088463128589329235150833173556905 +8706200507172140856416642848813849966143817717503023224834106896623433469273328907429832511563972309658830234487588093651045739 +1804318877742597644565913208808319750885352430439083436497378247283858616368363127977363367800390625897013789449450637875537399 +6640589174553589654051256599633381120024749952650719727219130629373315456037578535279963334633963259335915488696463449646684320 +8032272830450718870582856393550555068150335019058461099968557096935808325051772579016403379075019846185741776197086257884683319 +9863845517847573376326098832507232200317675290516210661300649571154879306959674077908278504743343209243187243262639791806186829 +8260961262731301702271912238265956345814430445731867024044874570864287595912998480366912356847572447005441275362156399624494941 +9393142035376317879394642824057024278760474627041377093083443136837548371844421180440200277690935722486852916560800667873141543 +2707649253179074679795746660619684282278565191941032558500021642302377918384225264159606437430918854686420568233535916210590247 +3653446714468860199818770176822269463628134871277975181151133360475139855607198727163601307481114241837610243765527921963130342 +7740886414228885927181817969815506430494309276878003524396743847692333838266397442280441863182363662933711077420022986422070281 +1048387053761801939044385077721021658809540040018134673396554017098633248966461113346771441179046101247543088625644697892282167 +8354720955462557218106068480057094392968433715936618376234382444557134795213907950353329761280756136821666440990155365084581402 +3688937719186699342108738462217437423679733279917547383863629404136782590007047571508080094271518356483515978166149181632884691 +9910698753207335191315666777695731726998145344199523600464027456153460564479006998462380767579651598842317204792886070420998182 +9896983218119310999564732404427002821382477892511011450905458994448002735795600011464506066851494867367124445888153203962666112 +0874480478255387464724872537442210377291532518746867402289144019512477208223281547993390653821697806118478800205575150577662857 +4918888004811316598554705337363884151483714153627256672377291874950758530975380513235648385297027498654040181963476939202925535 +2944953053399838723025577285199841577289179795119166022276762443360574684698180143394617215098184693212410119796009072687729739 +9732016373361334253176297777291060712041712200359193577310902701783613655008446771602140682798342306314527926659629849329119883 +6451043591004466592393679872228794008130850294984708968342075888842891418002758594861642712850414025146690654543853793479725972 +3268929657246764345075224814644064768187234583627518324258659649632755014931180023989253455291331973610627056888949488127145955 +4261926089033087120812284628614836440477048559583788705295941729615760122728002946792084729956587080892613331345225489314812847 +1986796721026437522158295162217239471552249715846680613750709615232262912940447904521346670150883469709041492285328187488804299 +6628012528405245613161440847234508491425287943434287101928325272535635664307751412665147263964282878231211570769131706300759265 +7377088066364420743764402212273540338705138073396342307947494852950818207406971919647576428347904811456431944019834650692515506 +4705138654618320095006319217530505261916581737627651424491878629844356338877632449493703212698711863106207638016952512058495841 +3297864919576913522726817303334406976314952205292550373391722910736512885151802773872849953849211298237482028668331950499038336 +8974669281536290012748118151506739167431258730634523888446083432703773451432089051580445107667501300580071396777438663916505890 +6676388335245038478835892287014370527286751599266433016127479958260673956274510190770236842963946164980795747343439314929899367 +9777763357901635768971296015139054097237772494695148781717193551641896481882800982585569826367439263979611639005318235314749057 +2006174408272512048656262525906502452313110102817338015892638328650570498699472308145823254807565718645774486952361348150134146 +8820513608849516349458467726176340280776183307750894733331062785346417632047203675211923303269854905166452369376623717371465515 +0487090344893360730368974812186113379856699749061983876927406730979994152222167896194935782650684486995334925005887216364226178 +4428212757677682587368912604936275746129828292671231661463255473216528603314371364809318568990891647756504476074694167225494387 +6438298664776502895176225722692218937876815974395529173364685280466577427859435428997212680686584089016477667080112002827762955 +5372837358048305432426520203755641518748461432567909426155727245524828496289926253694681592910381964428484012886894327170000564 +0966786693488185832806410657062413915673905612790978639242251067404941406079608751238017548545457392976940768385484687626020964 +7081334569477079676309855956739406608641536536529136201573794386634730088085065014162237881535086131012737674806790302926845508 +3849506256608406770319555639552727023819412680964197246748248202159679944624779483651489821124252171233790787024388338169387126 +1220350189747959004534763654451835109235619957413590127042747161319131890755258681890420398880573552502186079819673130304226319 +7793578559294710797103985830517016163667215076322029333261233893714376644214340079810529332625249572639110330515277641165833848 +3375779071080037456833323971305312807921291040172365572121296035080033198190386403786992644365488916964829938879099337521148544 +2151885079681150752124200634430960212055491111062153428630936693865872096334814997531820067268121582290646921550422149642030109 +8613181778258533141158145972750085504588401393903967265445772834337987637418600396630528732934239316047591685154760079805548066 +6810794208813864500809678470319323448087878028803605043954670396669190693495990898866714597544374154361810960387728955757997027 +3780694542345726440651447280692129044592214402723296549397501233597057657590741164054767713089063618201384926245188297986421050 +8252358803202927045538322743598848427122577241918805031148056119515867274399187693181758263084888023518690338982779085780404769 +2424326538477152923084594002081423911440158206786241827087394911592939363042513317947630468078474296989628062484584400644343448 +6293560419015354447114147053377323271370974957505862615433195073799026219744354293045028357502721448147460831465672262899873109 +7387532795839583928982541818961799651808690944192536299119649406867938059084796080728700163640618737891537102681397327040310310 +8646371766907348023824656340748360066056892200614557523317897955392332090329097835097131487719957799594533173582638863081864212 +4986100049536290446290958993632891990045407747006838784207732735249166707023914891375926737812449203630774188546578985599909811 +1084402346707970131458723746814930784514195757804466199671002857316090262762516878154322011683754629425143857675752082486603247 +8761838577711280264766083372187992430863964477324546898292032760669740251929185543394862067810777754782525582117312630882437464 +0771509220114585291617227270613376384799093204219724418023916278133523270037263368542243169870229505561283586085433377009674011 +9820385787390786107677870809083528732710164474423943477332838364565199183957564959608617849089060421328797474250989472544417321 +7919326323563029775076417955118634250595776343877190502902725431786697484594693383405493496141691115991728752796848704617855725 +2558255581109821405634179872876958166347174474348761073684217931689004048682704410888017280103004988712033725528911209075578017 +9160863221896162516421424469589930231499714756713528296148018288586763465400454214603217797926981201646940396481239125062147650 +9840316514604720267572846195066141632646978531870664811024154364403354983561787177052598257504070314131100093324913554509718449 +9311775701841172266095504345180054522901312438857477399185382790126084433098439433269362739881218978298243657480689874068996355 +3022781945908762278768811002699224365141074026805076790548873962428543441756247033480754889441799694147133688175673557083134054 +5661203741867036406194178717036861247199166773801082533375613708479585773697108277538910907988050442661518792070910621436071059 +0690138478379736786396005725249615711997663990048585261495921831819669914145515073403200894318211855461357229650824823305525133 +1751585791160648049215935161043755494836647727651069321059714784747155745680573976254930604344088093264307898664770655705644210 +7624145261014687805152488774841289678583856425221410365164555890159519557132962343799732888357700797774204345288024762788982261 +7890710552868121263477187455194185399264846143513087916598365744278210315488246993448630198352186843484020444533061474327432896 +8206909625120146564482691781469049022430111838502858582644125909195387775957182358764356323550045553074686821737193044914677910 +8204337650424296695493149708468179951960014630538528111508141341188662936175939750414398867506644825258652647525390439239752201 +1219359010151342200692679606063626284788399861632782010310804100495631217242235430375489322644648855854345633079496117764048987 +6653697464408607985860924202927606340854610960203650555980002938739009437996650927711204711526682854187368829518663287620378635 +9615765538591556297112167827891749863300345638363300995749126057833117511099109253123549891887992882557489784541036449471882403 +5919028352049694001658038819829671235731815415745829048231249060308117257367536285634944658365311943856716917740139982945041953 +5779687741861700152585779009366027675558545586949850924189709461217199740749305528005166461978008407813915390937008493360553115 +5834977903061566139099870190655547716966444068581774636647968617674813198281268006404303675453751976888820043577600761967333881 +6972326585063978838443446520453713997506879344217941897803906052743968972254016142258731934881056702957790715642228115030783951 +1362425902796546798395679404782769752464205460307818179674687205006891103510168328363308032810297518997174255427908231022445396 +1509825900031470252867573280977984303757520410764668672441796612789584482854046983756780462029600563288455226754517497700564660 +7759844224301976256343546717305935453950775711909213261546888518009701479912670689049983757648821113680742909226880646315005569 +8295567234561669944338275143058695909726066802894545455268764288094686590468443329734647916141835949923568889966174907115365925 +8847804451408263609187211738763773284048172606546780028907522556324927271519705281526806470352840988271648499699486513703844773 +6679160425612818347335886820513491773906491628651444000288856405322740614920289642723221399498209432232637228104522990852837747 +4111012689294616788593607591102956684483592350971102538317426326909441395344097833084048144520705500574792401000355907792380226 +8347617047251388711302856164567942061715329050448779661635598992241168749527006373179435024773116055832951174916423963970112173 +9055087726497472517559694026453105136312139238988721622285684818285622456138820844308772825216777711338810456475617988242424162 +4197863232696784218000746661193362219301917851338032677667264033511098062281872897803087758355321546501532794013889570222549856 +6115749894285255554690322957000949336927917916709996337643587065466062558943015944180187981070089004992789679317730749848395116 +4787717685054333694149897473720745326568218218182852236278365043324649884567032268831086174748464582606382556519285049880278120 +3971535819215057024510844131306800637142690099398881617684362973207491682213220003762765264571054110883901103595485168757504333 +3733461701846483183011223494998710865469936261566419212369452729547931045469934427223536598514620405179798084202385746863975487 +6787515125117652836269017536232648894348703796120091034388135038967463209181552965632410604353037772756879239864709539921500366 +6197228209653401639582453147092770443022867307115583008238408014093437109999520308757528533277994723967340185717046004119026260 +0327403058251198538854684801262565688227840370906858688450070334229654311591524491257725374686504434311374381373826709761897189 +6320800720313217016667089641041343357180293991403820786439200978809209918586099663595021544518755767970175732723433500537775215 +8586605557740559261081287090127076957830333678084994220662406403052293892606977228772765289631860524156764537528844813070064403 +8457029064238615286786107279460798384585105993306593540395560420933072225512825677840166549704827482169925869109982598418898944 +0346460363516674840943211123653705144516811133167773396076974206411386351987012791478690845256647926914987854519697980693850902 +4071039292859550793054081697582930309815712485519665021072773643984757349601035844973218854642152867868823320985485836576692696 +3566944195916064023455915853671413519744116499257428302391059795726588226281448949481887857567458305547585518788070366283222643 +4475630384874509141748841479483575783195093285020751153893790442309506156006253308563119939383816229293457633418322795791651798 +2534236711768923060748330398823973796625511107263972895904532310389295669901143468746002992855700338033513519817927059960082258 +3266833442506157497412341209730995861476441931081314990884968759007025722755350805078872002993770048685210269397083340942969079 +3325013293836474893127556216824859727670678446693115589979109778870903809827936806876988266059842178470200459657153466761585276 +9339215768239697841992780261560272323641382940735693971437349481277136632058838727826902049882698280374121176916226254815769890 +0579266322319718076679029381516879866920214093645961826097287139995438556892638562807533592029177968784950961959651824138364712 +6905593271581094882293836027956049322801541013654133342099776388830151131142904625837046121178740403372359293970069573073460036 +1334657560229091661514767192486484034874814804158436588838115645203542626667263606421481459341052191296393864355597357106139612 +8983996484498008491962652081920903788269022697096829121085293173454864230335343930546161539206227982332409686050424799265682960 +5979943191308039044837616371196377606973368879788064283861010090419804092295381096855027541458106648574572073809903498794247525 +5748223869403535883463902896159174890497470441638233019825678773597770107171367867175754297335334571074534607008535043181405194 +8795353535341345876592035589428503364517129250540037247550521782811791666374510791517112671288737422099652323046937343991337828 +1845836116381331085732349956975527066319503790930145646064646988078457512408200058689118375881261700543344729871472397774510391 +9908840174753246368222566017712326787176851291551010131314349894185604484963390215007002796394968662213178018411586445058609354 +3648708782366479438386590662421575557958786432758515733410030956830742717789884410593667200352894830011183921069426063462365103 +0863636445701114598272155058937842933641739192649108654968863927721554489981493578009581287965498827240184001075889430327826948 +4493294744917241293353606762738125798775389304371272784848161045309965481110981617696456977856845295663146310544856961560395701 +5828350063875380289041787243953420472517640405923728557572182762701021146981223831971084235740009591348792854248246191617472390 +8142911427486019533916938360973756294199899650632305767565787069658787674361642673323286198646595884519076147156672045347769575 +6919034458315451231488534732525157456948587079445950850088410378838535977516348080551775162637211781455681436512037018188409356 +4896222628077283599302550538329307529265839416312577475209070966536398530653252477467412899117369638571925172776374288534229909 +2854126939661606237890723618332733028245947026454912620083338662672080884075389624244778233027185136679702658181696482687520771 +5486768970237811447646114465006645965128132304833174901323446834886512598955526763233636026886751282286763353050412483339880879 +9146149867375327619762240148102487012789152939324347118132416925423213349620444217738724989375651879154078406666532230695010261 +6432289704281535683765846177747137478857199113830933135896448323481180075359182281747819927805855289891296771338965010648932514 +3498778884149977137993362603088225708342761654666600988476000304267323094698652989046529561743798096047380169014973930605911712 +1555513950736376520645091335370846495268353191373549111643901821633347913274787967705297774512956543219272395543943905097818713 +9824515005207942672277682844175672734608049189360837751069564229080983682875340351162730595292836110710458156809584248318311829 +8644392801319979903321539224060850887494189887888933968978507838319574610389051812420770840579282228222004146980291262806411960 +7170669714045362254284955377287230002812263383500586848752066007279901210618595185664394253569437180949208007446172620486446640 +1197418824383226696240145317846075038217263309407432715400718235690780656588433075117078422196797078588171900080472556706089444 +6058081175983404316870378185108517992309376387807189465675421456144616633676191879370638648087281836756096354696164358804958793 +7175300097350528879964995120589418404250514397730435804307880003642423238815219743208925752113332762492815676234753855313749687 +2216821005143022212418811965038884804162533171952178272981773050431225972337066809470007006624527746167477143326209744916009321 +2874879795494394023670916234680048368860577149936343642623399857065876920729230526286990908222249218870734087284012928821855198 +4892669237695278094261059350844729450055740052868656916863083244548039117228735586420221099748057054754127348342899320517500476 +4913533064284953905878883623486045020791418669162484614426142342087782118817607434327885376004285685360966673400518425200903095 +5236853047264130432207807874987854720636307899392809936830966680235406253508899133299685304059306299982669510153104336419138281 +2635729217127660380760950574877790788724558832203965184259818062945607402185371541729168619518031385767332811305256686570812707 +2508693777459660048221434065348386546516873503781056818039736884680690592725066422073878336659156014383855199604525567079024098 +2527847075125720859307217125972603526269724367871881994978039745425984811500976939283305909268477371136914408464862183620857832 +0690848798245605847434249059051242459537031098176625887386313497201621005955291382938621297556760277294613833001841927601417783 +7028637829951952641999762601338035213911204478792655772152455046625599760300329777350980475951284319951538433620056110253564781 +6305383937871208842219397097772665248616270756493102017303010524320362272253687689906320569651514870191751716750048983549016387 +2503753802832702119325330648883246982515086513140942076923304514033686102637700160768687271319427097046984664384031053585769263 +8859227992404387206024381418823288245956367455432601449940099288001162654396819305512052575309088805570615034227083337938687529 +4650804026596773751781171691707686499798853858855531297662807643678150070418762900082986264876734988853277036828880591174772676 +9388499761915323706082278918760363181675840316081077581226482199990184463308418161663917171550066696858396843063924348798640785 +1339519437101345180144205804260788988338143185013195486237509665528555982435461459049630103016492555659996037806521222234571639 +4775406178328515482958996000719481194780201067828314313954411507309766697327602402398592936844052573242705274081653994583193860 +3528381321380368083594961454997422094241837847317957074543832643804670074418168234251527826193469648447514505161752970468032251 +5644398364843284455563221983222109730803753471463129763873997470655511240512635865533704491564823907699160432034740568090163512 +2019394829622861074339274739867648100223183661088275367604560854273519173101761322314118755022999112270121442893766921444175618 +1024961245677370458394203192048216519899540056189972652416007572725591801341729111029393594712226889998156435621046992279746041 +0271013842729391960551828591764938026380238772791709945105208575380084459912538928688891874547881085895537996185248760341022722 +6443644163751965239785936903668129878996022934136734635016147841655906985263150160687560568016130320940069489352717984945617017 +3980979401244807308375088961101007696165282732048547089807678715095196311609438432458589671418362252711116197050123998645160718 +5029882228039159089170628100687054725011048437854579104323727026278151658455314659996721845525056734089399419451901797673293081 +6285409273407838783004808714400055644398911907669583936293776767386307697838130877184976183405711337976463042849151509723365986 +3789116449777854153143327216807443418065067164460291516731014023725079965308205893342647719238044202553135045734641879985943779 +8298817649314977181973643591691453651364965447794449764965615478663842601481733390271221156996231579991946480360911957202482419 +9923477919534528913583979154285960230181626227828624997322864558028580882337999440750509388244247586570466385544276236851986673 +6266991152224272268084343794242067444247144028060688211987908624847699372423422643841707689624467016900013832905272766427426840 +1355067343839251713050029297274635544953158759007585898844352763675665452680269506694090597773786276467200134181329183129591894 +1203185079814519449730972616794089846681228932967991544425024262788862162483806670387321965112895932667954720701857303315356633 +3010223406691406448732071777784917670640450961064883921707223591238303897553076936064137492313930301276184114003498965768546700 +5055662053584872171686392213754554115803665951484341184481678793730256377197108916198950525496472627926989148952673813222574403 +9135301836957832126464142557708338619444651703425091454666943559384577035607172295502116858050756179608799326777436914985064677 +7849895476822416074994849352092796372963487124157171056152173017480389097568380804863683908585558640345480249639539155208814003 +9261731284405809837137712563712141454654064296465941789875748814378776568326711784539691121352690345339673695586510989770974288 +9504548640513288992288616894826847158596027709105403654435535528958145029279713253496007210603612311147571118952157286120765811 +3172019293671644099810154685400878900781863310226630973663196219151877477083273521217193649999031944551410361083160211498456657 +4584835039206895266033291139088493090671186991332152100943013968139485825629047583367832339953098893263224674847060194281239454 +0086703131238762939716791409510475698044261061184491246815071586146930458568320808697308895974335329174951890370629879266697586 +4531479959438670471013925537723663881523195629756820131576957595320119441034946472734403049979337927590688920672812290238838992 +4555784600301091463114748695085394421438230893318590726172334093673394223159678897396878630225087240466235943323892469006400425 +3181295825258212728965331361945064921751996326740368121061708410146864334211196041567878993280564094949434554682791503673171658 +0842856419453623298809261289857239402872945556557159985688775020115214582384781860334689651493902766220136835999359515682473907 +9925790212755349035787540271290598847673187935010889467948737594198142964082798741209190025028872359505393133888993203341623719 +6881035084279659821037463368291359288972809796102439700962259394869871767171237148065599497261891353342041591198086826562479936 +3071229176011336182119446125694433597177517266017496957596892485297788477134388949109492032269677674633006492819820954408939636 +2158505696670981583492100994722239785061126885782859457540392078035784726890160616966602169759722004423370966068470851220475055 +1608442432121043588281125489656099832629914426535320512741673998725277196703980523715247319219104260826637474643734228192860916 +4370675331345005605018147797477139808981239675873728794694700612511771169428567641131258023552337644203474230406734637422278712 +0072859216860622480346667080043679784914202926542586524758456470894276035984569755855670540260788837234221463158700441389231519 +5908942320852274627106540557811105888610228644494103402263750822407154871367868699882023311506835501899157241704189414346964284 +1936077419723541310264634055462791796583876957815065743467706393867078774982042343936087382789908364464848533080889495741496533 +7254584486083999860932338903788745031776193198150907253247533783144266741445178920158676792459480942978555519260352341102863868 +2324388659990555102109355727630603564463003730580206681960957998377155433799099008210231393030776738444219722412702108302468947 +9926613338642730749665937063588945780266089547872794933113744630542358661411190558266352099886620858549171525338223571617167711 +8986318093141580580876428982707764579287478469073583981696234648037808185140269756651566921560785874161230201920488349770275568 +1902800771331526181562098287158829944843731017880219524960324079005886150803168559590390474829249427791824941894802034999528722 +3088584384403360990907891602519470935005839028378253171884990772710302832296914348751708792698002306050160936562533914379396209 +1408310312115069530150233048818331378284637723150482392301602795737916335332815872089409379843010863320155338585218615794879180 +9677693345069319403701934383954872660888681515065440356051840106884171038792426226463129841417745680354406721408968771559961954 +4557539513449236642758101806291205473048350296831633561735117237579593752211878576489139742035316348287283326781007355249045447 +0883189604094854361797387824593448971971447378881204753543909216846733915206018358578418844775503490330084862478510316277108659 +4392550432502243314906975265564518025479113098902169136391369512817565837562370156090165943691065253900876426946219189578736203 +8435865573452195078786548417341611434423772217538030617156223901185493595765324312422048299670372764076523094427788078350076588 +8758192779916285126332006796232045283091922027709795938540004604312898200544114013208612412744965934919541932903174519980041254 +1905758872835083202758499042134848212716811095613617523820839042848090697442064092232729193928479222578050789364469898951584433 +2042272602260087723772848808702134767220173723358646135775389214656797340171301999623954978758165595263461874252751080813852370 +6274949154731112794816216220066048303875986872572001517499070940234617966182507743418989487331756345308613913050407521686762445 +6252730979488500452149052167583298496816203113490366754560247487872848255355376484138619549701240217905711665471993273764649239 +2281622687072260169420599936083131206454896277499378500769391609676460024680767553958496006032193910593446808391535507538458599 +2191010913898494379461759294973512837440625801801770581499807998348601134363013493664904191350527640096566620000395093356134597 +1660938804470213933500623938584649317535645503810385086838104030744441292813013981381095457570526776722828297499568794267530527 +0049591878181449823254506571309609592366697654558586847111232650386057621118329999460228856081046031697258812551653592183584022 +6258688415014551491572224840400910043380364244249549379338382107530054462860121947719561782286604468083436850199239844591653106 +2286199429292183729722721966817924988059655688863458946473056847154314312282407066615331874808857982581754086039286737461422747 +9602373046472185399409121350660074121130363291629972502459350277036971026440212505022927430609191673069989761094096992091670991 +7527879946650183351260678785085159093046180958175365176059942440628342275638429735328061936632746613485734744234236065653841137 +0853905941635849403951594887838110573652181835969202362039487221504242797299698627659839204874816529447544114385531980406666739 +8062623009337523311886378539061039298966733017052114071143987228104904834551295397466673191503750551657795489921304516776700376 +9007862096833941572335165844081198462047136819292917564132567522715598859947138927117299956642745110763969251319858190814717852 +8138958664951405431541453825461330550794221363483549976099721937150607149588852431715285804748382531904426919820630714135461983 +2082397885435139903190314072146037819079478576247984221600827812796545457214101194496220097049368103574832514769701472385466387 +6896832416290927778053721602166743175733725992261187904101998561355457754417056692268681305083190893002118259512241765998122185 +2636968504609327449982531058224266443456972154252698660084884189652857349507395676672582020179531698016039374573532855093674020 +1136026577034357822803043248989730467073666295507784903395493121839773911132783532427651934810756742564183961335402101819870313 +4917099632663820977131969667158301701831087873930909603855469814730542409624643679930249460380846245995843352815176947927777445 +7319944731827475004706218777611396753968431393783636860295774463198452128755272715443101250558014321901313052738593050746571261 +1707391395815003284391913583753339859531852184651610466904418403752429242924142601415582143805388089376849781779454300111958363 +9869108151029888337540428241180238714963181644033868424652184814249832670934245802862990793758097055499023243559198701686575666 +0187337650771748598070996214382876748717075026419386245187486192182726086987036097255846930650043060886509574171932845313409481 +6310275245723435456702127325230222604362757109683102954203602714184216564293906952316506569915992137104170152492029578927512300 +2305363922251604346990506838961419388192179030700159072409204210540634525740072656419492493606787180195424067534096459265589672 +9014680664163948601215499948895470475969838465633327315676059364457476028555130517706511356929094761498786703750352514595140558 +9277484889618105650227338993928615130344847104186489285414725102606461111639517477747291575877009412511989063198428666104065406 +5967555124952181729420151522869271680843682059283488086279627160865021360567069797183031645357984651583784813809428355166017913 +3607122306789653495447270140672966362274565501229973599229667563911606772081598572491953192767189192005766778789005815828610262 +2082222249317420567583150769393704141643717042001944511315146960110297197944325284159980684280693676403148609684883994123930097 +8731372124960991940185405379357412214932260280623846110357067671501251533776628907031655983387403452673941318358548346602760124 +1976021347697640428375581819297827333264016242688348297392388997119461967525771833238891591593742490277240125530759579876491077 +6072466888350935400785328899402008544459154165636826625026958037650740601926412225205263947146298128779293383020953022202454362 +8044034560774166401511114766914479073842481064943729623980836043134237110449322364783254518711575480004977127696613076710220521 +0564017571074921724777748862479718313010297525498781712387929033234996773668361156592993020275412793744070740135811199115493778 +8419964042103133610587792208097044601548949029168486670192222439144702607241592021192461521821947279760550828661220709620486303 +1088814982734131440185884327578226836944316287501836068430921447613687229952034388566585290882851495307655765844223170274340586 +9005799435401477659962899657617685769970912656575731463413483835844226203192655446200112583102919467583122082891247399200176783 +1851901588702515045318382295536798092875597244431065042731697266394552848998020859370638943554279437980607960337319274852691748 +4157648096188098267490705839256314766006302678665206847589272557754575914579199683296869793220703658904894112352768597742132369 +8723572441033421822131850984144065793127526826673880856427241209325971643349588168485181604003549286943794523645867631822180512 +9961788999122788832400015923298335447164102572685865228181427272076170219015236471640964326542574283167180040163937092128142732 +4937228274511747775697611764578391450794749466871545726021901964860582325608076362650424934625277209649989981489750831225970741 +5984541310271980053712817247539372152339967892775462123252335196274653874061592183413734875908267266920758029830479031847869228 +3871371830143074933411065987051697909188835376373962685096571028998970101211924718499411402849248202339306457377199877126871389 +0586547099952007576979285722825771295071780721616176263387445055392095793671230602073249233658371312728279604207471382798903479 +8923577166944872998618492077791653391106848257807366227129544881151900203904339062941439729248722161475876280041042987870134297 +6237224656556330445619379055992474217471454724969182529987949872722998015945413515067495314489889032179791415649686011073157477 +3003735334796840694489912665109880649358659073702150516968523096869480344821929684468744926264579118587187974938771984262183130 +9005753590334142936036827167253181517255166676962390793299722321456204920350620126047032582007457817410580787154332130609567063 +6768747081081207028623866855036005032042045118427663709885224327750939144410815093506371403873404919815687844934325487062884410 +9939375834444884922802271908890569123942112471025359287053666892829043053920922878243233005965596366467035614279935114942794959 +4228677728256796231027207112191813165638914358249269082781564785198933238543379292199026489661901488181239514397059686005148486 +9858539705692720243176916548886314393535919032096542151105543882501065636878211469917818253828853172821658378951042353768012326 +4422150194025015880156761058170304509425792379790006321176948407538912546088068547813798000277530779888405928477531694464231990 +9405650213736879289008838391184069127301195712710013925801226563743554996233482554161918019454022970208565224871459380574639835 +9130794172100568378934252595907690555743933712658951585006391044055921485334952756122241280828770097902109351842319381201019204 +5387985013440379266230518099534711152536460959269911655397255034626367231069408969413762813968064236154692333107664286960252886 +0837118680763800551465069959598175835527467398611671504215567143391659195926190188250898459493898805547983688580472819756799921 +3903220661160023222988830014776994424185300024466935537043911148899196047568212527843430862631973366330579525805431481011727438 +6908486037368642919233388174527626955887348839535505480142124695672332446751550909649591921180619995217291566458925431968029596 +5922782959375057616516378703156580986104482212435661177276107010237081047649806919032851751263569965116169589560831643100864715 +9530369870366296156375956282332841862206458152569867266578623568655520592049072587837930649946891321926334502571392866551701600 +2244120737959672078726164762991283327806243441107419508409939471693616693409630730506528517088302223393878360169050233927904848 +1173648771365582895309968552846929556758030253414605906336548466169612035971381029462786542081116525705113252983939162686385109 +3234155840835288921699357747660762996672264982174457829675917923339715647526557875639742064383339589189545825487931401530995501 +6887665436803180794927923177816956708553785264323074693008344374916559316617894764427215802134641031792988573701240438184249750 +3839539179042881405914564139952865952209303008409731497507386952470654995467985010268759732897359668659271914262252626200604099 +3901076984640950131960333781644223983532267536121313347358475229440208418673453842347657506344090138736051557656821297434982077 +1623302641135870181311409682200177224901019997404700215147219379739297672524540935209651730504196225871915072497095486880839456 +8525315354478812852093356397223120386907828133392270722811606375757983394361481913342296750609970915686272616757634461450159831 +1758237483830901977324883248068822612744747099358422855339260889327800667524049577373525949059825015390160043063670312221072626 +0817646239152869131112173902854770980862701701464607169211772950069653293230684510278012053087035337608494974396015461535406222 +8646346589715796030816122730522353992998487085588488148264344066530587174128544229529685414458831257128748560990578804587360715 +4892783957104711928531643465336739867007712821809759300577073458900066680389265377353301997705565938884080611069388312576416304 +2814927389151100129042633757615682635990656949553591616128856450443418496471442467646240189440950521511107482659253501599167684 +6135116334119022986012296077975055833220297155080260438551907096128989879928867449175546615606752828933549694825257606317586967 +7016532424149169529714586507287588315821039767281860867212525388739062009398857291177966375322314074508942295009826535646980545 +1130704846522761465358987904002837038112315864342341500267273512280490182439691698104185495677754466174462509539951393350430049 +6239545452060864474289817540605900770875649791603624701849860428246844860967392240963173839859434643202570338001444647069601535 +2892256768296868079082781775690843873409200886346998104928582896183217369070621962623861890767458984640863725872163427837396161 +1015987233236680540127175130575507867333440719260832000405122222181144252353002520085749044160442813319007733231316952017735844 +9207122991749764391450668652103974356514593013095625877412964336676292277200308352297929615022995501002585354045748416214004090 +5010570807508645991981283973843421015013551379801238469792061968346208698033924105198332080219826873915414042770997813678587105 +7079764317445013461353198084076807018884451655619023138435629015461147032511308684817994462582990789356614515379970417351047112 +9725881506883451817378068264565401988152381111185798791532824098438029254469346518084626414715068349480830207550031462251028658 +1504691090829009028694497048664643697386279163491642380984787314683252474807267403308118309227599112336474348292331546087385709 +8374045238326514850352601439525770150385106432876360117179641265901618172845807500617241643296725102357385086749024223414320695 +8751924711372854963768655608131773099674117966941670496696797649406607434691461307943445287152706412809962107879642575743136123 +0303974873561466480968743445273196900705427451656962370162651211903195479518829395352847623644931113876694882674530035776397562 +7062275706646629882491960277069009716984996924439240397590784333436313250958554553224460478301293892351431447612983420608299779 +4021998564670601304459878729468539224393130081394097452696802435298562203672631426231047502881058781296871522825905804208338252 +6603870763394362689777694406864771464497251023441752146535341971346977029094361489321326128197028616266687437237475295317363781 +9156020188117437220553660891624641158468448699614504995148457693527457205903178159270764484168386506020072977552224361333700137 +6800793585132251294965485406539737210356860431874461572263005112556102550605119443415354337340356018964066943951864924412299915 +9176918467253843792621229869758475550809947953676328982327607776920014653501974832672677357939313552991103607402118582223524673 +9686597280549256712036750220654564584483912204263497709158167656617008449221355316543489486938189978195357412962551710086697028 +0388429487175354035020158054103632924527177834523214082900849111108930893956355794346651605160826522047668675831813135358949190 +4345101694469873179969332042811556018423606297108948984557961826035019597070396285751114257989897953296299318364378614357933290 +4949793892609892089229851776026934561460317731511255552266868980253783700717106802153694611663582999276204920297363252094371934 +3674663375108601609896202396621130718019413744942494222052103933540452779973933501106619925311069127219850907767043719608829470 +2702622493254040904859153876396184987380395898007693621320399251663876437827374821903867019473977298529862021085159803043642262 +7628804430192406674682789691745692230306369379190920544277057351743344722619473702262372514469670378356778783948762245486971339 +0209360041654178829792127599208780855624409037374530019447406585398770659754872987726192651460619482209256452092276066423476738 +4709783672042319775995641852494077105915433063897114034300326610462577068371066683331006070063551265931785531029670403665686700 +4587667003896460717120912220270889981167563292172474600789871114756272885374551741764032376074283701965139574996577462686971467 +3848976224442081690472022609003207496720929495444016043645809920259850317579964113261377231461428538754038582648249855596427337 +6172335672098748490848298205075949543310509985651798361629655073050078700179132155025462638010290501496311312192593367695422649 +5726384573393389957861087954363684393648224844030047057212547523809090733733447320925926719918185564312722906061784825834541657 +4665490918503684461998203394016725914244105391374685986007968106627529172068516391306150797382538751326421691707344779240051404 +2641961236056444783029513171308212582932163735856854734301365261361011265730957017358847540597925397759304056715637265630175924 +3143119314616534179607705689766936638499515116711864605104806787742064610741185862931157231900890917045797092416341390041404007 +3447931172643093407899075956016941324761309449689246711224867235309324518097331543836073039264012951510199018054862544697302228 +1563566785164424421738528286186718541346261171578886688118979569809183215506908080307606078120020216388497864724764057557754295 +8570889786931244194585660557574434060147391448332058484468219132867607862446662006468299620134301595151046310252336800694251427 +0217713452224504960721091364387786965013258053103569599886166960333008816549340813042469646283329089138949995982273574974215868 +5241363057936964400802812697301682184281492778601196784957952838829973528993067785327299171845639921011068758599950934223980914 +3238619506166339720940794008445086813283016112070588367871958447351766426343723002443074101469688320695818179265745434758910694 +6296789680362763149161376071869217199662771928863547592423030630832336241218625794010606091635986342053359908043568336874619758 +5796450483018638379674081988788071939094611782491186354599577562913830254367966241420426194905385522339051453836242132016154075 +7739106134294761084702741107955615651072406250165155261201936784282832131480551569227393410480076418655073059966098939360374229 +2971567365547701845803783070635993312664824380381023912535236337973918393748125160984639017693662692774791712740599492208482304 +2270720904622266004044552929046382853488397030569658054130832432158967100326401536494483965188041727224259066348964879067583812 +3443273270836744114169351145812281297836105568675664537098362750380900035541986757124762712416133474277811013584828903165701857 +5742987445704693742780012691654778768416076035850059897986267208927114643299349752186944079949266040605774961616317552910045136 +1240573283641630694358812830029096031820509750547875205950719721469746524236951030334909216603563649107173637973757255912252238 +1182181309135378156725140117731218230316055150298076621940456444204177883491865113883523054776801063888819486505485385264913416 +5069068146388574137237425212952037225400371744975152454660711335647864580574001943714998326035983681508282903233341771279568745 +8467943310622656536228892538026964919357667642832630682651110048817452858559725652837991052187308635773654812551494387013316194 +6440134083862480163392226300450457593962112666903507568451147153007397865298280740371802995538252604177619078977124651051346524 +0537206772933648445348418297957633068071300723364120286017796847418742109018037795201017609886014503958468715621463005263787770 +3432430202920291906940764640954727327787760258258401409130943189103009317398784374181000619017547537895364515789076658241461815 +5914935373373404632064261928025631881068410722283620043865276873695737002786518062855969064577740316535280772509182462064465423 +4663807875203623784262624547123565729447932492414543360035449489544264792983057557527120665531457725601958452570287012086637115 +0847474551270439717567791117916716520890209772268515126243374020404961236068669964591032312120458856151122774707229179093256702 +2728583785414355071386563956562688209782901746373262288816864290881579324219562028197868974414398025024344306680144426091625030 +0910829767068265611067617086376443471268114912189289789288578161296371552096260414590275441364898692653660772972831143055685254 +6907552020126899986435407709381912657932246467804972165765350364308400598137400904853881086873855469475626923756560878726730206 +9692227748520363042215131444256441345425607520902904049752565589636006403442938604002826527897011684910364030121356648587505405 +4764025506989776287250936345001157192241256998927684892678268726606209680040843191546472014984020704327316352200558172668843000 +0564503985848436331698791790813182963520559967441699554268886929254925633860614440306175833628563304317763920599751129071094309 +0175566287557279025857970584297243285600759435104151831150200001725607992873593941524916608511725999483859291253760003734973775 +3039168535120967789573627753936889933310390638519872912077737282540474993630496322839461848810678646381314301370434612679202453 +5290955424609618789728513231303753525180260090737043547498273247450093929519045624882089783253628274100484171694237532423254859 +8186844862044986653314802892230697717326617551815053423012041608272398516591496440428962043914698069230013270892802838135287280 +2942112717174269473287862314996229628874313075502005461595688743732840097634991389852224318945641142059923309434297254976695326 +2444525231843962298275031069381314438305678331941237253189959661616931936428403908777740475422459580996785256969423138340880864 +3680179310627154010609137293613898326769365871171324366981855057912989303388559760676270131578093527806064422528032847530674921 +9355461118722504639190973677696806900807813249020915988601326357024391608006515894656601520223498473406143763133984605551760635 +2692839783034034225135191314183486878688437669767719161941937422862653760924569831725084821460846216076710658987697058689127356 +3364076178555288032798058397361297765867389733546080821420688209714329880465424718281983378741966307965647446727248970877582835 +5097154920011309551648620026419749934729546113147672841385317729841476832905993191729886098778334663461209723905553676387326787 +7544038203057755483948118938898802083672133837458011190981229303171157034238018580240354203030584798053880317332961525265980136 +1511152526450600575245224610801100379737911240153008627074626764499468695975779959724757934622234502314480579416277867481722879 +9208761275469748679977983637248710137550354113725122267268928582906191386758550109656015747293931779066403562806751200098816649 +2774371960428126935364571996703554555526862912399267217503390978028481995519553561409174881144281743002477593997449474113614320 +3283118387697337618913008345214573134792212133551845640272542182183565756097005950085429574121104212579366752791246087630740431 +2251626647794796903046417171601708089033102353726791435541243544024082843362831636356376144988414260766152555993385246920667885 +4120430579803988192186052185336786436260523534744839109920724061087523726726856003315563861271636470368332064799342319979701963 +1178460847308168095740461067148790390275190816124283621727277092177096823111400887004927669450338704196432823728793780107282726 +6147466566392578561700534815888328209861843963585876273190658740165656380640111544922854734365367040797352367883442096872576161 +0655254149992445465845820920429897376775083347655359252308973271695549200271712623988532809944592500421544522541040542744651771 +8895306524774577797925949127567789976094837338117657136712633080384090456221881872243825996086082147423517476054977573177854050 +4943011385246149882784947556915542478920754812582717541224558316858308474902436139789610336015128572291595765038163819815572793 +0059519490142292292642467486355644357871443222407272677363258010549846343690410511589487255437861629574221414317395258487976995 +6701428890977392343133134256358135045673673123158832116303929634062094950503008501398110339118434776130231986975400156621462276 +4809112204042804726404274446015570840535663272343219408139885535879522105193162064034829419212674983141334515042809663864776730 +3339872236842534294842100648778519220364381792736266281427277880995347117460315427394400908444602067720779504370559025897150525 +0666515892628240976196163023835072227181720931267205367481763695114784962335391370607825220441784558120081030826294430035145825 +5990171621923618467399400881105334235398291318764791324436841208478784739274728174080402657482105706012062680090302017358495564 +5800215155555227538662802534678916888507797284554941341847639680300462071370712985768701847640442454877861723356457893345549450 +0044372835991682943791367236860846326267985956993372513320157207230356385776345982286545530591945027191940491720657627496371424 +3896431871159669144163572836324937737933585239956934241358540063356296466656555165061326841756648545874963895216091891197215432 +1150436121846416639357239779525130732656046114770001886098939931426129354428928259723637803567803101198938817466591948073654097 +3680333838160206864509221215320873122528703603282455751545397796927467717336094169326962640299653768153008615924472102530867643 +6770050643269265320774249712789524649226429216132578192388680218997460246361157967129125548877818859824163987287642561738796200 +5254046150685030619784205850259746232647288005274167276588953501245671262327906633892459399349248665683674586756619925085832749 +8138659441098017401153449742392984232024716079021347321627336986000612605589176674195767657558188526805373912850372987580206973 +8191739475819996816485468724355025309335206345790673864079265551398839303546015301031614690697079405116794058344817857751170058 +5907733664187032783992586304169841870946162957846844623230375351751398150941213221188521406317499729525031329275753209552972135 +9176007624492506072188440485436086995355405038611315169617103420795642254487038087275622057368505174049950711687063028236514019 +2054382102455304130470152360854926810408620809647732343016107388521668970316223874173994646760209951211428665814705542349614124 +6010507266966547829863386841978029050310064720150129583679215294838434102788460967870976228374996605588773396271433676057289822 +7051841412308687295953020826737760628903413518500966433370038504670238126933304749517485860557144247313921158680957030845329616 +2360033770199629503223807832521648961131842719239878810835014788911969787608442436353630358920568228223028658040834409931217202 +8400997693934004696590555410764211165674028575041130093079293183383749718414250933932351869983505392135593326730456694681893830 +9601966479081797142729057700273038016579775812303462568369987589932810195102540708796028133906200420900692102185183676170434137 +3162497370959471401670744764602103343725945241209609871641803759642428128312023652491828868062451307296471228615569464816843941 +8764436487815154921343571706294950245330020417561336192910050884197950522678946771691143370350696790098631293864395203215663546 +2103973531056753606278933160819590888661208989653023859755262642075600795082475977389553769715231157147229187302967554388198370 +5511263147464601148510010944839861149087736327442223343582633059022202904665113422199247896714934045390179111585670544907398745 +6503691628140930095878935309503466424485388886339832328600458315631288162890181616057639363435222140281269282576385033650710680 +8454254370393439429040009560609022779619788056284224520991161927353411842168395626223590494290282053470410540843450407987926472 +1336825679355920509182270187363852809609898538144457557442453481966013998014732868105005901821373796606751500831791325616529203 +5075118330341336536977717509239821947081842843667207368538939749602952519028901304254002381236022047524790390418089280097834547 +7087002693940685994858725839374363930880942852293359924965925052590869503751762743229260822706873201650092697209849276502693145 +7306083000612674447155484200177291509744727003567573915026132438684216504412932709214471251820240604475149792423248249870477794 +1828750345492089414352178700274996717656637631789061874232476374372589026121784200003799632748786969419729627555600483655428983 +1419038605582900945308948756881180557106644399148478031842482768934058982366460243721966998715761966320233275607842960609086807 +8995674775072395937621113944922442969450956475559169579744736615353848691595206952225921509080923588771792361528472949801095259 +8071164393093218807836177998870397618178243287174730595843254576188688725044187468313573761652235487642008727514328298453902298 +0249472421551084408818726321837167660068433212182140863918498142778473568559604363644028430498972834010437189971676814372359835 +6326215711122626023900879284642848379045342532983675870120785391214889560676166670449442675798343547201651587376372310330260972 +7721128349367234491190961548434288775058240627555576639847795303346657927088312538298685663289068965153779510427872075661840307 +6404054826004851129562529146035965093659305313594131922007840749997852785915480413483418179403471734301385082608331943522033365 +9733723661694274986939415998932370600151791965983225598026282848806043753357041009447678948117256393500635321688798336983220809 +3023589135170563869561312040343087996767334919517638058142422305672311345678035221436322642357974773060658747505711133889241231 +1715152441111565992971519735514891973779991073609482568950260394204089469526267416519043590653468197703445241031998318436540928 +2571161687670398639649025206286059219618343667065497532146361041751088315806996390315937024865023472982359206847895714744923905 +5104856285757044366877940768642231044057490201353054349716001939164041109356173546969025877651803874584635636362767779008126317 +3656921864783698643075571406752016536758441120671704957195627803118074461526120679475816346733176098378409722349965248254591396 +1131443502066788861338718815939549028179403039813213172777877033453234020243883715396074872970863142546452822669185069959899184 +3828769910844736669658151259692028871696603091005326667495981150974845310134146991259645265882236097619326183020932698293809389 +4299190605742357746915386971343055529972124390299804481008342198376182446383261925929177219506772125075974364335839610948010146 +1147666764087177430198110110312435274731031579695008275499991697667495238558041459480531076474591001063866865437151222907417625 +3118583599222787818632900781019308738830040805599161996484071877144944130661026998282751748208682924005109173737574254051272267 +7248376535827628635806240316333585049198937287552863170331727642858039762809709721675292461634836370227092107000659180675306750 +5306506627623780068062337884640840561088552876565604494700675337745509144650468822636638698294453362040534422173031206225551013 +4252579308534696009946359817557643262143167696605231236359117810698816646524895198187607892477097108960784373761400944824549798 +6045449932335488814802338206074710052382801251742328755176048705595171441928353779134497634248965450262069632636038949744898591 +5048702418591439721957110373280102402968469372594873031440576890966279374555142674908990757259647232567240212811527932452059539 +6445116473158402926922663638150560761537491240522157144544441504700466911488897981059188305240921784775901100861995605851341634 +6426302952753103378949324692632848243080208624864503932730319385221633332864218296337728836033257029589381148577572779689079838 +8323351033213327371073881110283228611521471023685177945561596153462821296338506612181495253533007296541471850977144060198153791 +8612240193336900124220751825865213792796718254928148410457543169565765686381156512821242185857562689521331372459681870446271120 +2190046770619246800670427384048126037985607253433475697587520496744179478706647309818755439429545326720329201172200607765064227 +3736184856673482672738889087427014657874110745468152414492168023597661163943017362999706603380493625843592458433287608755926735 +4182865681401614720438201427164757077032257161772988732629306857669039790289782177374506368443513627759379215518499475202046737 +0697566863573366009839193988358046426820897141853585923998497091914817356341676241151406963597520055377063092845547594101697448 +9770510287164808256026738019695567751276805483623280478598925336381177794945569958800804342768914035606587017994256909693642474 +7259755154611086941695255874435534803016372667978524879504844920733928636400671846029963027849546724083520281554956394251995619 +1834558695670914755649864898844629047200426021450454765139131674876570357846943407935142270035129746721137853801869970041785717 +1163596604514706992103337640896730977362836499853948987124318776421720842943109071494096297915872890046802390417171851693498985 +6696873506860390067654571204036998680608912603658516851469732917336706912396900863519518863153116679969409193499475151912748965 +5772630153097284117632752487886616553301227224710840592032831301443809681968538716385350119500103381352429438906070006049324510 +7255320449590155185339594353774382542901912346562333365165993746566717810379624006716063017965110258762932735063223456498216112 +6248309487192090873881009634314790029246770378814629258114526635202683322798948006269443152846896870196478753064865917440440787 +1079237303256511529274839846470858035223497145513826461412560940325017754834231423361883666751693338383865225187216612119160015 +4861549012935871901840283860561328549067295489644918336638431557892707608901138017599623121810750153111482390969696067122547247 +2439131148441383234199376711842415663942174805857098114579871600691759196917341291603802573327405138562282722593414751206457843 +1973359935209045256671529911507316084304214657632392500349841226204006608244837820483129185333807659672251730206136609040933140 +8184943256874541309753395121370721662620513066937352652876771594490437810449353276322830929392380590748672786848276069239565457 +3348707026381104836690412750227080237859968000391150025633563394577801401905026748085615086352851413067595432050498561008571529 +1877075667972855586652155165911443897166143801340467259805712033287524340520335275436517702758812944056177253493418193171478211 +3774854369590560985587771159245595691913575127970445498501177157738494672527414527162836050852813684096791146417973149365483037 +6704552131555310027219368793280881514598163193897955852905718685913917573430057096937340959824715120372466589909944315684960960 +1012314375846207604646749185426484171598374093369935297204972798525329648052886568477644707532784462941904877487489747098535818 +7985674284714583813663198777955800874049562438949351977791559431711873042288148908962573859023348239888106006645129281470473018 +9254850687404139400561382825175974131012329412859199274634520950110017567085977669352012785318630525841745235957179742713982397 +5097303689162257167085390435448404263178199429357071875772715774544733708487138600770119134943129539543347602041901144161660698 +6953181183113203630678272715731094392489433607360104735402092320971396494926265205286840279679199637087859730680473846380532205 +7565033230336604389926974822292700950491849340699150720775179860790531037997637976970549940150909535407513425455328335706561853 +5335368312471755733760541805372968424193070274491865313428788002453279122401760512494622819480016105587606929438634932015824839 +5559016338160351661853688320039654018084969722536423822579994200012801736919709694310831609907486201946501673508356764142877308 +7309191546539260723637253628477957548575327772499530308454034050815650925443083418507106015799454347735223841084017225944595226 +4885590707691548323545253419048477650698836445425272829459034786263150039138385336170173040262385017882023012446601786508537725 +3159300795992899648874466508404624507417341674483549750138299511054547355595935184376005929821246479521411349307469101710751249 +2708412392547375974662585574062716099744667256888486220537125770970468132844596575936704205294434437646542619212125911160088350 +7671842861670510559275736664436986661209239228900436842961873275642777838933287515196746049787877411927981310119576400549956135 +8080578380146388148902757671260230173280743467984486835312292349158474503715607238743382799824745964056080082925917323860644616 +2120225379169780135148292573265658608927358543522142151484031366388491618786494744941370627406622031907792989518445311039563243 +6616422310981362438162790258221845503573731451992162868203601740290116853659138924079987226446597241351646843845278977024986205 +3662517640661488061073964114703410205367685498808240378692251201166863090113025726146269622096930731719426952075506480919216671 +4823643457908716999726557603223790753609939423361772712423018042932577115436095139459068730410826645568806300379584835433450946 +7837676208477360145796134097352072195957133041368812408314333472555459775670645841696822521516557015970698046160780950308863139 +5015658403765286649459660652152764801017680991103667912937430519322186712931073677796456215078436603241360959178629806771058487 +9139921128024002577938454673706047243620589115000240242563021016654786832806633509254081676514023612790990852595917560180876986 +9308821204883025320496874045455981233709125160954382611635568126874562312377547977111739040746530325193651004237373402504537024 +1600821697316131396065115975797494969376263815891380490449703227459763844691726208596151555303610430036532384862800860173068687 +1677749011530966588655330008218462556189908006964342216676682876215290543872066380630113889263010720992320394875173725292986311 +6989579261987669045792589013853182107944124182584456537044269091571439065320682853599165887592383283334586801282099984394392022 +9970108676029913047954776468886108937529661369633484559390003673582932372797656385704195055160295258401674267521236080828952316 +3273802761420937902459164150004835781371893878839475301617292925707508874975102558388990001315786419341804043737604037226331752 +6423865093791333509337877707981916567162340796885934470115945789596479789320653277617641131277451340371426117129794514423620761 +7417609321834214164793102707841568625675868448913528641242723253295495191476269874558175687931888576975179732585669568870748778 +5922089337143793314961512325922930026645734559888635155138481555659443901618808201784727019382947895019140665125084159195324704 +2832300412363246058905063308416630201890917641050411420603351067801736891124820850891397186056454796477918095532199343583413766 +3842538349880871712437577001398701888177344881522702272474861972439339253087204797211344636084653095826769076765966355059432859 +9877839498202302366738674681533509411775934456542381162185787945980551914209016996885507489310451917077029188324180322160260432 +2738878897545393488539641042595991720479279596803533846602159358145022104838297930283221202267258855473584700375502341592186375 +9062102173051107870783580788794675784912283028909776152369743147402812551267763802726764393893702046520930303504745751378996798 +1890158543698671287785156969862722836025364759849336645190863408137034806850238192071351320453303572785270717307117565718365448 +7734822410504169526033583218346170435610106103279145679136488844339942839493180608845290017442034674025577300763459944350044123 +6105040904995754702216898271414142776115817083443246834852579568391309141621714380298793479424942862844759389997283360409266478 +8207744090633188685115276563756960203356370566871533095111186389044186232441179082078908516925531610020829064403059153715127765 +4543411669625489070844963508370766554494552051662899693726257799474616282059274838670819153036396550543481066774790569529843561 +6679799651036598201198464287278166599830430479287605906552407498711145045497296895091710143423667516678729308766311555682391918 +9060618446032966067396024483927774799271013972963401931215359008050857228820472502367068320857075813753280582206475097498167537 +1091834892775689353192772704190529392359261014072163618697328287354942394830079331974933337512857161868992496906607708010073398 +5364177554337994653512816619520240821994073186844625407311661314696382001907220045017648487411581430791143507856735567122196867 +0716761726451434192525856251744493844256079661428358229774069510316066898590543756088198375973992063361152435222465923662351366 +0043075513893517349925411233398957648025096112448548886678072991181902374596438596256297098774636238148028169588473828501924259 +3228516645884143195864667466124866845099402543607000135666586926382976262509368071827459582454935921975303881150829096536404067 +4761157693052962556888050028734514069905365690844215214316263823173976897101122538440327270223709317772895230031391498145736860 +6385041084622772519780334652630409495229736116311103527030831268982936563366735822928512879378203876837625284271193343076105495 +8825229991999801614347782438381474867852878074358430022437396713162807802543161593009219312576950358519990247724194904234397934 +7703801976947027234960802861233790319790676821468496141323682139060568458559647230530529267618724263639883376817372167884440632 +1726382143017323548108870010820096752312836426102452450577004449101845698867822824844060001600494219691513701486376765683162295 +8567344046439933818792026021934172754782928648841755910876641284145941585066904498876784179002951113808421135227067381608911653 +2236981696764483162752804813037353468784452076739585261632267090804876266610444767882886160729217192498345880705115733282372008 +1313046830998564657547840785183730714291004250275503590948256987454019097344722968276149211207582987088443854985798581722550023 +9063006406664758572850022859344148370428972822517076773683529207252067208935050546823673576957790680951020623419459032466629849 +7938928170423206022080881849589066531884975323736816185913375521669990715327020849225286810140422793741838143465182856377816749 +1265756842388175742586708059491362377918488379984875832030848543287918941984050070389106581268802594158962582407355351035627049 +5900221712437059899622129468224343840497771816212975521659975058333318690798732737341876177817630804815720469182652538833483463 +5823081639915099173836211725045578069819187508359785075471465778986260818861591546963352470179514396250583548650281488440780418 +7451048170517299540822354367273854496577812259623792325601325690996083083112151683648900535783537568629739368276233311118065909 +9084734714613092289461178939653038690130944168935857129538400432419569476969252314501208058863985767404598559353985694589773895 +6277214607560240011967714168838143750302367595949536752213657372427124984199709048837186130780108450270594932950252051594320147 +3499572321088417920853211811454866152496225537457554175697449661675274675801653044218258736968600625898775131530277707042339508 +9914225219653958408220404955503421597988800462882509224240059485399789816322467527449242209991095322026817971575335077154317985 +5278081500892846628897442768350495212681297210154604830667333056587120720522354514329460517681155334733974540010110756237411297 +8210591237075811936724693019310304435150990235368637778807792760313222154371335709089813727563208995319466715986861635780866968 +7932415587435911762210037637958044637519791039364219987373646146500303943912999127265553435145017969397164413915816752545161718 +5633226031400350789184112011648210881013020831437183038308240362781644283374155015679893305443987813011631237184023671550613020 +5811978080228290171238069706605813947244025818423549831774560590994763911374360626408512454006214897383037052649605527910939842 +1578662832737601740265954708879993575835470810729170613732770615041114798158887175252200232752097837948350852301754510131346245 +6068409444406865116594371496646576550470605636603063539678037419994055698404550683594340710909264161990867009969259182486360989 +3207255402572128867597708386762297362519921843080894171603067736003753278183424031926979188196805787920726466251458690002582913 +5008569364534625206093594865389861974491057988729642501378201086357626134901180260883065306275795854872408871966970358994916566 +5260870280380960692056382943423819321600456629197713258529958092905135072192290151897612011642141070383635146934909632500071674 +5103943101344555719687493820066438065019249811470335254469779344109290667032018362593087359223818942995619770011635033602760781 +6090182191502983738438010896590866621707943859659299906860770756177151013807688207972939771298998138528654622092955429850905653 +1626336772125369872523005387925612023085181385255260458074393235131647700974860337649978590067937668268507156384372177447503498 +0983088220975640726485831380650348574527016263360984693517646111104451963030529155545125362875373684368744288268392996464031787 +1886577864886560438915496753180364296870396016073972814584707172757602216484353054194841318491639088652070784986581587056423963 +5791295955798452846006486957884980659241458306340044696019455166134558465794056766142717707049752847050948964499027310212291201 +3920118159051788842792018209898781792646763214510193250573669914454162275192491103856842226701566784572791940951044194717542411 +5568425247114245514540844570366702834465456557122445652286286614869534230920871827766118797945440453053831301603663599611151007 +9132285049335898106348530191639824752961756612096827245060794650887002292087021431444313048295402370831165699527783501924024187 +9094264576555332744996979181892440388769625070877328456545115444598432716233979462060670021904359658071557042696819752714761359 +2624335032983933790947468174067700780434730280286305837296068262033116687738482347338258135450536831314508366201724139587815922 +8450751384747855334597310320588125802084226440657572189418133991724784364158225409421748567123411489197359560319327166452150553 +0217315146965805878176534045386582646736519057669936823885725039838691917663928104666774688631639185290691596050519297245051431 +7940957013433199012061114563562278128253591876081307949974283942244584406336676182042124767131849195077146222822951756668914757 +6589005956437167623060683298552386150740712051896264556502825752701728084586373026820140301271827258402697782005508526864025995 +3287288894211530920495178421618385358928209601522908652144815977173646436635300032777312309529559530424309089857544514218168330 +6097483328468004536631471410495796391660358670051698715076410248502303660016161800128810231929174703618080502422800241933644130 +2956392224013095847029227680428083425620941588942686433369479814315411693725173853101111802188051631515982053459377355277916857 +3065681935968907368748287973480293783213808382487086015879418794067438804225330554229750223350574245985145312570124344685276455 +3489772935059313564818602631010215526615299221297140830316899208960430528821219213991899112889288129943720176337014173173174332 +4169981630658215328566362976636435099613640630157506404256612156966851420692144492745499377619802506248375541742742014068421649 +3723697831339666759125999969874386488250045997138226100381868783065151573396084764792101954730971063666073382268822664370648202 +7695739092394145422409597895972497681500338948172798211529024633083754418602669479252615594701113082363308526566375385313538692 +0362024243582700927996091721261779876357892824920462321831613846936511858949877169168914710103805138231321626700946199996026230 +0910370398336603134902288593437904852970471827636244578441003561376156126397047213862430430449454667576391124197185148345471598 +5028346423876572027639326365391106837855425740087158529449602646500698308039624506892347711322718143846562617911464289132024496 +9181010776426759091535139982513243227866023899159123168014651495554175929065144279128447458894513674919834462361991122972799428 +6472902683846165829490192503189706567753176937559527683694767361120216602239730653948099377610225798683453077405631209096231256 +0512986051426035486790488833906991553793296020140460296126591780195814787329857676072922129878562425136025882880600984983973989 +8577540121631192413042201337007929873227061283325668352988181356692287167551269236062168964313569524237889901244379824573640154 +5738145734013901847027973765976787682776812683421111101939034836663608749968365412229382487929512185391212964474327857768911300 +7562207836356806317921677823535902895646261255973726228824124734902468677105214682457780953879753523764199156291556619449942205 +6685144408630396033374384521831023348395708816803320958823787110609396335187767019703187308157675845831700883961350733812554544 +7576609828782901349340272529157644117188745883108516505970478068227119245704450471362770646680973620118556831550370068906691429 +7387998131808587529731801781598911046784196725282943998624870759033897379214694560999940160160935915403688360574784745832951209 +6248576464881189569842693329569180599977760906738941622134746107827192298531798906163755685774369390363347404463590563452485459 +0534570888244129725519284614466994594106901806409139995601464274045789187815608635654309801136434736637205014712781222746921224 +7852721958335248170761829133326246797700730414740667697834509581576475365734827900004460335634027879413672459976734498386195835 +5088370360545200433735093090688791665393907039059272593032100436084501418577206986549399579720624028466951070544585214365207165 +6435187129368614449937580538296933531792687165143536181239511296901417188887649499928950174527974591005214327286330726199831884 +5864261175041853165789209708620714822361777843032399821785843235985913827357647540975103877312914819841724359018056154229990484 +1936832320986190570216413377747783409617074645446275675504398782817281167330026961325947267465552277788937638823143961712338389 +6643014994016028760819796829700070956205113377466899907953033114796754643995517690305975245695734383895488045495784881027449542 +9277149772110266765516542199009389979979428729803481968242094114554310414335680714665255584623073310801300516821617747378223514 +5122582985363185134103472499467534154970594419472963412571152321765481331992341844237099548533622925469977982035402603376599366 +5702039170472823969912601643296899307773750835106819267275831078282467998094066465029472821615682366477091470622470049719032042 +0671010358945492991105729283517652015315441687350701252921272273957729883415005085140318681745681298665587818639399209287652478 +8504952151973256765543113775190946452315557877268395630007206112950274261280515123602637574614066439170518948486082023049534264 +8318785147359309244073883391968368385030412213804815219561275632927036049996919071912768810313118985299449822580618372563128123 +5447965794084022881336224721150430474622954063445132529997642171980649882452452874371660709010271333178089011623650142945593513 +7498570392070031176556263724838104407401975655807188803601068318852915004571680712468602581133705435004279673319305506280782318 +4235251770160613000830130983063100984485301111420207302571915861863284245935054969687285090784775829404258632032504825073887582 +6407248272722201582954889873840796724288126199508498948167917317499903480984445960848498060154084891463403285778107348687889003 +4953941330396268815099409977237615027521463257528034758197037642240947224961515275989640733601052280398818164960445717239008728 +4426630124553142687273205330644281511721380282097626819427510520776002629092574526215543555846304555626987349130475830717662512 +2679622415503744585776560549326024439456730222065789254179516934309405880579097989297964155354764447981652276792153719461894070 +6549452078295195952851972101892868752466073329081552637836554870868329745518970415001754631788228704301414087107528862687179382 +3095714010946058367115405111675588241660359378724218257897889232511664785827286570897205547229279697287663245465265895194075831 +9805465298621739062011152177896594580714703932025865262409510756369094551491315344002976208292437754134178483455044310999687172 +5635853083465443752399385809613532628000929554168679920157180193163062291041706935484519604033746044464716659982230583915172977 +1397085494342438160066950214161726216316797535747161972858218528442291370670389809452231490128596440810108729216233152926963393 +8718938957831579949398261083744948669476176529513240710990560722588644755296402086806852345746538236347718030640062631744819555 +9204989031916871283227517942830744003831314993547893753197955379403077174291901637842637655437310798500528596483721526174221178 +4510621681082500438705444746385938892179962383128718253236867948364044872547102979732643922151263657382171953178711857903512864 +3434625913079139632847162412927858083774910063803492872036606235419074389757800463882617655159555904544646003283714069095719127 +9422478480758793975004764634088848124200701219293815383644557399151474644429876593122326476162568364525117348282565684006439054 +6415412788931044743766779624337137193856769272687596658353208728053926044570228051289974744446738511898266628888688489033821326 +3857187055764486798882511807937125159502970249818799519647825055901143034107396715341111836431533491237288074873324479254847609 +1944275217447109672414805304798039892872048551668309961639053276921203694927946551682999672834794575568139407200344761188832584 +9228691574818073805866351085919567356330517627511140717776424135867621344167516969731782408297076690641510925488519425339786231 +2831239904815704920016412032913992948413502229392159186483640311971384616648328934218616172408437024641339341534952850417631408 +7800709599423404547224077175920136421077429331304204846667623312748910375272177983791546978316206296185604509872479031731294077 +6784795879386865023891006814459422181459242147354385223330262696056730516247499993593952360113613078289229711721789291466201911 +1558378948527819137236700414103122090944390583763494240148255786716027072401897565958932034145583789267171948650451004479200278 +5903365269957297061267790413012843976619330012086443873201405355132327717135407147467813950090532723390786355726062359803253439 +7629287528827946090011668159293077488731123501345393936384022425933097863996545653037874008416991412735073016080615679561676815 +3237960418959875052085168366152912866396485668534444686608623522780325138572281373258421046475238324527598957994900036953588632 +9883457218912143598750744649289916045155230666253998012127292069605955397882251577278616463161982956480851083203582599294132885 +8739605725967415654345383552272209454198363876790394143551885453633595477692847087747048445889713841117432214889427740276964122 +1808274594398149686259653460592417452536265711085053636183889817336547379067581749366999937190487929782077243956107305394452893 +2858306534503555758138192142342374976569912519114070071304001216131565171144485590883323767986065555672710513575942087665489357 +4757258115582473299797872077008431880804766429204591994348523186470015406253571125582684142251809933836340635261386229168816736 +9918555292750664784762306372865895993392873092492120139178044227677315162874458415577767229742354738329991177067335396671747156 +9915416169565774505455198608946138326580556663002878103141442158950284935423931276623631554768708518659417736110868144091121408 +7485024844800932215930566743200643612646843426043824587026297306446562930746643938917961270550045568670866510426684185886721448 +4920398501344484751530060228048138752162844188260697740262204586350188489261031255952257367650241279996726021255197979400713310 +0983903945365766675649828378701431579004857069167244696371192650313930100887222144395396003002837274898192175630515559492616368 +3277569441388072350944394279081011821982637576381780324461779633187405677617459795408379063310477462661505822241372624056397980 +2796195339856562934096874382820282192099707799004934067504550995736513197478848274092619553245513516215920528915358178045259427 +3947135872153511247151284668940911981933985156149720562664695031030349744372110088157223178406720212021960705751648342245477823 +8276198303392861443910819050105767637970397653898744484200995946877348235373325786715347188697829025445506211703973576651655150 +5882576032689008918318143363475006909501115410116941599853261283394744451901101910507633092078494500214632287815925595536310673 +7269241743888980922214944937348603959760552838607959592411748713582006971089673926445144319837129979120595791416170081356544964 +3311215693514569183444297282757452024106434743342415838798070572517431245405339373780763180400593713529558089381582197573125752 +4556878108283717136754864320383151773421100605854818531410424407658443836201366417280243912680383714161438889747985913112617159 +3825283938152818066103999819485641639048153915591788440799327104545750772861261496085802570014233362502408220258846139854490942 +3543037705426699624325696485612282411023745859388735341967646420985563945181345313960301854453402790876180716518598169356488497 +0055015399759018591540911328290609447855851641187622864276870240973176403939507548117764594583218172186919643238977138111158956 +2302146157347717928300258596580246250649908644352652697865369736710811431250945078622499892640698932203651495464626034751579299 +0465216581077322245370388400939469195489616593652097640401043603078806904801413336072374703228412879808024013691780444525050754 +9151727760868811886689437563229171390965138504950138407176506255752456724326383897323049258585289255126700265024238726662681731 +0098281899254404044904741984338800905867702052389334861534065540083641450439874866038625038945827791603161398328731872690722059 +6264530562489216043969280034408943083142765136686187837798682491792210809601751533837187815038112873969767271421029102693650921 +7670518311894906730059859298650685509520663627502700465860614043829130684092155123515969024766529965326820247830045239762876384 +1971827918374425559499103800305418434180845901819771222995898394549615763182780848478726307902754377493105414864115497763562067 +7293146824231836764324358936471102982745003549454252992884038807314576844595566006507289403671236553892547396334547842381936455 +3707012658412106034610065750021436552038057251146149724375405389299956024255098042568008242870114719254029045709055991603654930 +4543749521242879802728408392725236169206369176046813175364511678612424177154836269263324760122983569594445005273913013269664551 +5817700093174352547416009939112502133121085536020612539401905269863944981266637678387919942591042471679114800424206674615903746 +9928390417067700668674925186060124842260894166668581468768675095262594932684909013159566891337437296287921942274275342136808279 +1720526807320641693668140417766107453595978158154018754776221360462162393984770342801365549647871062173743896063916005240392253 +5161802386530577435326894842370519298812054283209695964545050386222673476579184628919106024296128426895608065795872894836272477 +9269809290510364760281346041981538977140734635535715877861564862122154561230471413453427434440292536928348809500100637413855174 +0657501300841813074935854851050163894991818276164751987873048063138250805404584707419292446697964047248725308217946641983844311 +8063478785147013188508380911686106501438761736788881545360437305874844044022285005421389985126661063912349587081288388003345466 +5080385912620164034274035663131591931589292314684519240800453976434478145811035864455399019210261903984106215732682395895799522 +0939020059406813121907073513079575778716665344964094359764782266431575550874973231091097656538411338304404129653958312868999842 +6732798933518323978985303323091966916070134400750942228988862433672654847071015762525448860254590537863809085645618465872667306 +0427846766351683194895724811056690695837239230026578986973494357639196637007765612283664140977141629336513743191474504552821790 +3228155682944358310924291228796943417467684350283874136741330013804595358442242749651649995689037646356343539795964269040555905 +6826145487746532059740843858150338835296508366342272235981293613561407683050389559856243442342524690245207486156018191382164551 +1327436225221742602074234993619036983345996345216008664910823535986496447271949044534929738194453356131648251656209560053354614 +0407290886807276555505472513777566509192309345071661761386510676177956004136233159240180697853305064135526857625954524710679122 +1431829687708285619275876929673787963838663779016128696006658789855078135858492072768308764926825429711165034052613724177837083 +7028553048202580189681827868563421125284289066186493489485470631057243002992982744971998313406439308961352814738730662842546474 +1661580089658576037549874036611720164076275368701553799369961428692585978010787204558502174932891500734632543309652429899761996 +9575856899639421670892327891209797029282817824375074240277705275435359082312316792956844954585652271368228646822716619012170165 +4690146795978834583378953218274128128954879679834962721495470431115747652300230454490265247933455376976025418712883919785265770 +4032584676449148273007032590154174755542691092611500872882432669889260868344191769763590664159630537010368972815582431630162563 +5692592732110530293190433213941741940871216528267789341138499704212959459631375151781937020467215223500973204249438057106028338 +1541214818072382006149334712979978351719934230083959861254175062060206728245934867359880240685020444293227864055618527079589472 +1439159705776204064690488345048299759772753492079024651663143703086754589449249599931908249754660757813918172727140178033198709 +7752381183500066741893133463291861429042158260704598873907314527361438231014952758222297919601552459311683576335125432073345960 +4158788096315630807867178963280856579732177587633674289101034755608242832707107748491269005626588055241208204418618388578035469 +1184416032139115028602210049000593619116279959516862765906011705138739437332038056873961010636018153261556031158803924660448426 +9834587809319862013631879808865489908412831755556199315250744409755028410760389172898604230817719091796600511778684843691064279 +7878372956127669698977302339079670592653146391325443980293253311254734925614678750486528888944734310378404782150993544089766813 +4482271911480681906845899905802627519765113355182171607609262740960268207890619067150293516596923655844247586174848972864721468 +9552003526630991149245651109495761934587784128507108647124483752936558263106644305431230364441437558819641682185001628863763328 +9406159315820904843204929487522677107941937076263988953382649335050929875295580457118791755887589396355358703932935557257606830 +2969493527901243167542109903168828628101917337300403325039813279857934939104326409957807201457446262732489661524241158979368142 +1833967145487476098973136162241392322964264292952490319901422154921305200229075272133864200487657774340560812039475075659326308 +1445298822625141627089594319278576573094647719202883678449230305616300998398218250958603519589651710614887336894157702428315107 +8911019753589361230572967964751571906635823516517521975275494263948066958905939807605210390275340148405377313480598158489558111 +3183749609096747980513864052412964607501407465024842349303716927046134047076464086834827581454812880653390898896534483223922702 +2047220404370553586495348686661844320582343653984119122227275984849504305399708851775077220602618170548438073982557547603633042 +5484875538490583169391720574953649919139069950121342298729189319935008363006908499817188567294646844902804708973781249800855552 +7421001424990409022663446235185073936721530809635245828159482916215742480785440259606637437758227633658889520747280770098045723 +6510075506635860479703887783619213078585799368504850589990946726994909109916359460180863720392604659818835051505356439580781411 +0959165291367850253561235537386721365762207442766902579631829064237987397582522884081947056330594794737026301952693630585102680 +6267281924773054128920399667616892395906931391070201721710734680164804934661829697133693962106573047827000567454860592220102084 +6351225785373403775734572811446819619019190839393144125015360556285521375890386016901082233720031340998488590003458019399286006 +8086983669350678861956334882898616321436714400384547300867927315159417274826609618496301870293049853960758679069277021904136960 +9865037759852510085988938641077600576537641357106366728530348981390205491195956901092388717027996104357442748303975082427786786 +7507248774842522159480897459767378325396763974897420116495369220388118224704985677597657612836164641593242643705906198310715923 +6020323897854469493762019753971728667497338537420217765895884555350482562903234559598367966289598307357713584270039680501758042 +2970682482901424731104641663111768757582340710358846705532647449734646971482108058532929343635001949578504462729212526976463810 +8406644630764947233594179128313043631264609165757699872474368034583055609364865453910940072958562666156078109884350362008700655 +9106501316131398908994689175684333822574784889714347654697727209736986308940910431037486363365667132654371414795819563310659169 +0137910472839714679161637852622472509459014895991142745425159229935069751019575455036034662579025492228994125955644298201357138 +2263663590472781654218931217059804728469684174302359039764853349702219977113201315914188584584955078222068657185984247622363558 +5695450824338155861977114741562006985207639935376048162114526556989656274727076392276059583216303897816482298413238883403403632 +0038975862485843345228895776545691226502347548540923009663654944132545600827380276828701039586345737400863528654887451057233197 +1285772332269416234665537910079978608222632602148452508934499816027147308767944385913555512454637610386856292966968437445496548 +1261330191618765210009782014085845073559775965093415001665110717532374517158996315004359428915981278340620344383170562473911188 +0755483943667647640579099438554263999127548734337936583257964438443463208543269429173892355146856191237124938489437234962714824 +9129303544439064208938552091032859116474732583566245269345387009587018850752964356086348467165770868170114265169858965115946288 +7279622065007376862875602615836848329672073134886504804366938182665036461895999032293214820132094992897811456738921945565996030 +4834236494451378286976564913106384851412866607459667235230175488097172277734513000370973157558689076209485032473704000163914074 +2906852713508769108670361019771730001144071549164484831831379004788411810506441972725735257048988599402331202363984166872400551 +4374843284474261559922201408952633150671870157517305411040726961843624607898601648184347808307024923212656070285299487288795732 +8117441033938778257340076295628604371272466046790715921004843853378457388333862596453687093388500288301007959254642621709441228 +8336419205482595246714478040728678515920231077940527413442313540990859460129785367310524821300597125039005330560255760446207185 +3407569957745240672748410048085824476146107644741791845367520753456514888607145322239479922742610070735465164422006326154369011 +8894279996606564122245246476739025836636961740221798143384506169709797876615015522415620193119156094455114077417058486277270353 +7131151035004395224877087554662361771017971918239145723682405673667071436258453271309611849685373102636336093172827546720740657 +8432145665345782054212385521456100690288045272048653500410117161030682916822394178179943934653293870095835416016828701651397729 +9807379993673315841883406823522043062628694861759093682354411179777001696451399181533177926653740164108712792568617753057780749 +7552198060883514609633356358462724322863739238474217533806217264897669042731651388657558396154686026363774639912794923252267503 +6291771443785740732590982981719905524567339994411151615939323485160260431350712239547320702908777027534814334898810771281406391 +8383725943909858701756050979518960102350055348726092344362770497395426060226252186716626586574811491995835598427931237638307288 +8507265667315900242180544608612147833573934012185340789193936346356941286917069481442515796977733286907179390046007293777378415 +8688298391307343027481331662068268845813946664921272848764804887244314914603468930763919183778517730411504413547554154855526591 +5310568546507610904043747230819248926074837842921381365456929822505503792963441504203788828448597610860322974517666735027993699 +8190512204618058514563016675739391233696406975567198996502543146118326981238682542313301537034074950744994938638623004424018628 +5962797353210542352910227068452267455627736914233254078275501019026029219977380762852165264848013890176220808626487330453830607 +1392715231741123546207682784414187261145744424815836598165304144212321073536110482416294352751979189695534837507189501132643925 +9547196873348778230662094351866444409683500705986806451682346762409749401895555972393170493498341109191510645940668680160665767 +9096955810744288162025152776265225979561511052982298941267330985571152782374720461772752985049704669084322641186449467889515212 +4360617959299125510229120098324080014685874269740158186892010379118371918256414709312045505595343772662619039003425826619680875 +8242651081630156918805572079983037638369031262141838260427869758050055377235638965872717517504788429694521145838583513263909672 +4433259455372438105974775408627724880736064277338137893108385178827104479149305047828800826485922049778207824411348676510055718 +8718825984885049258859271868511156347573558547616835487996237695354831720772501296121171500663173314753159506044722718279173640 +2709996430110563394535123371054969016136174141380219513450493806426805948941155396288218826477132071568170532718023856256400877 +4412955918580719390710872526700485018418160226844433869987075876676044792011973789419662798626013873183203506118837233134120481 +8772819562426148199715932002776622117594716096791960417797687467419822085071157480539482033047913473604918965910085487367720357 +4625085282706988812822930138218466374524023159847349699030998731244290316248877616127354994943514784160430249591960166158827428 +3012297335233487487686932791672504848939891571342804916267615897166810277253170819233520663757428230248104405100538854660927429 +9123919446207336590235297840044149569787830675448852079217813254125078565940555357151112153929694801251132171203795549941163920 +7476175369012980559938118310990110840174599144325883878065361105628152837782089093135806082805172562067617245231771061838879589 +4768469919871257284902195592516344833223851928691778696654759047583702848545509080899254152913598427216007302226235522358460750 +5223927757227044947866784478462919350895723643402842384138354740120256079056226884847266693352002580953694527348195695223607106 +4705073558122375415417200921965563405533347843275197814453910296613637292816403322858277400213988846375567124674868467361795016 +4745728385156852832029629080429064757351207656991841817680115495315482039644479548944488439788311653187692817074819840714497361 +5386314847182104599263810424039609228765878994123179215715094367283327303020988006589792974721368333841513658700314945433634456 +3685020221252259142980926069027074077129168059113331677771782227727933843623094566219277157186574916058687802285827552124321505 +0794558617931775588320039074320252238014802597836590634824227249703483529574186006183975509386000848443692647358501225142361167 +3942679708028160995866607265771564423203677879968679929985370044267348240320617033379072586756373490054605230219409903866795469 +7536599276764983130315949383875187945686502994166320326189211280377933687730984141460802381512939077916932152569652666724046409 +0292377825630966235158683241512971091944539929600065707699283877623772501033680549048220288927590692487589517712276010516389098 +4141777627623117777410741182897322646935774432337593489702909715834793849902052358894916963594941828827118210427424755222982593 +0263099400776234259640984881940007947968250580544744304077736076557953616095573458814306253535718606350692981623255928091096507 +0588327094996262431505059042095661828596107333578122924612340170790916991622052806671333685104366780235136494027277232872825498 +1523246308282008538061408160202517775966824053244715248653671495274650673682535042749033828795099010214859776685551489176975506 +5811209762314534687747281829159103097186141033482266881873367741901377849867980657673782571505747169485443159028925476795003998 +963436785863456565690613348237130676523955561372248056220702510606833697646989872355666869136182759934340379879840576201228LAST diff --git a/test/www/dir/index.html b/test/www/dir/index.html new file mode 100644 index 0000000000000000000000000000000000000000..be3c05f7ac78e7d7abc57a2fa1d766502a3fe975 --- /dev/null +++ b/test/www/dir/index.html @@ -0,0 +1,8 @@ + + + + + Test + hi + + diff --git a/test/www/dir/meson.build b/test/www/dir/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..e88ec79fb83c4ecfcbba90992d6c7c6695fe8417 --- /dev/null +++ b/test/www/dir/meson.build @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2021 Andrea Pappacoda +# +# SPDX-License-Identifier: MIT + +configure_file(input: 'index.html', output: 'index.html', copy: true) +configure_file(input: 'test.abcde', output: 'test.abcde', copy: true) +configure_file(input: 'test.html', output: 'test.html', copy: true) +configure_file(input: '1MB.txt', output: '1MB.txt', copy: true) diff --git a/test/www/dir/test.abcde b/test/www/dir/test.abcde new file mode 100644 index 0000000000000000000000000000000000000000..6a8165460570531a1247bd99a73b53a5a6e500d5 --- /dev/null +++ b/test/www/dir/test.abcde @@ -0,0 +1 @@ +abcde \ No newline at end of file diff --git a/test/www/dir/test.html b/test/www/dir/test.html new file mode 100644 index 0000000000000000000000000000000000000000..6d70cd0eac27c024ffc26a87bdb86a37aefcf35f --- /dev/null +++ b/test/www/dir/test.html @@ -0,0 +1 @@ +test.html \ No newline at end of file diff --git a/test/www2/dir/index.html b/test/www2/dir/index.html new file mode 100644 index 0000000000000000000000000000000000000000..be3c05f7ac78e7d7abc57a2fa1d766502a3fe975 --- /dev/null +++ b/test/www2/dir/index.html @@ -0,0 +1,8 @@ + + + + + Test + hi + + diff --git a/test/www2/dir/meson.build b/test/www2/dir/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..74fe78341a83644edefca8671fe56c4ea5d46629 --- /dev/null +++ b/test/www2/dir/meson.build @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2021 Andrea Pappacoda +# +# SPDX-License-Identifier: MIT + +configure_file(input: 'index.html', output: 'index.html', copy: true) +configure_file(input: 'test.html', output: 'test.html', copy: true) diff --git a/test/www2/dir/test.html b/test/www2/dir/test.html new file mode 100644 index 0000000000000000000000000000000000000000..6d70cd0eac27c024ffc26a87bdb86a37aefcf35f --- /dev/null +++ b/test/www2/dir/test.html @@ -0,0 +1 @@ +test.html \ No newline at end of file diff --git a/test/www3/dir/index.html b/test/www3/dir/index.html new file mode 100644 index 0000000000000000000000000000000000000000..be3c05f7ac78e7d7abc57a2fa1d766502a3fe975 --- /dev/null +++ b/test/www3/dir/index.html @@ -0,0 +1,8 @@ + + + + + Test + hi + + diff --git a/test/www3/dir/meson.build b/test/www3/dir/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..74fe78341a83644edefca8671fe56c4ea5d46629 --- /dev/null +++ b/test/www3/dir/meson.build @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2021 Andrea Pappacoda +# +# SPDX-License-Identifier: MIT + +configure_file(input: 'index.html', output: 'index.html', copy: true) +configure_file(input: 'test.html', output: 'test.html', copy: true) diff --git a/test/www3/dir/test.html b/test/www3/dir/test.html new file mode 100644 index 0000000000000000000000000000000000000000..6d70cd0eac27c024ffc26a87bdb86a37aefcf35f --- /dev/null +++ b/test/www3/dir/test.html @@ -0,0 +1 @@ +test.html \ No newline at end of file